最近接手一个项目,出现一个crash,通过日志看出是数组越界,并且直接跳到了main函数中,没有定位到错误代码,这样的bug是比较烦的,最终定位在了这样一段代码
for (int i = 0; i <= self.cardGroups.count - 1; i++) {
CARDGROUP *cg = self.cardGroups[i]; // crash
}
原因是当self.cardGroups为空数组时就会闪退,也就当self.cardGroups.count==0时依然进入循环中了,导致数组越界,这是什么情况呢?0-1等于-1,应该不会进入循环啊
其实,这时的self.cardGroups.count - 1并不是-1,先看一个测试代码
BOOL a = 0 <= @[].count - 1; // a == yes
int count = @[].count - 1;
BOOL b = 0 <= count; // b == no
看到a和b的结果有没有一点困惑,其实当我写下这两行代码的时候,问题的原因就已经明白了,这里给了我一个有用的警告⚠️
int count = @[].count - 1;
warning: Implicit conversion loses integer precision: 'unsigned long' to 'int'
这才注意到count属性是NSUInteger类型,所以@[].count - 1
的结果是unsigned
而在苹果的文档中也看到了这样一句话Comparison of 0 <= unsigned expression is always true
对,没错,unsigned就是无符号,也就不会出现负值,结果一定是>=0的,所以0 <= @[].count - 1
自然就是true啦
一切都清楚了,@[].count - 1看上前去好像是0 - 1 = -1,但千万不能忘了OC中的隐式类型转换,所以这里的0 - 1 = 1(unsigned)
for (int i = 0; i <= self.cardGroups.count - 1; i++), 条件语句永为true,这就是闪退的 原因。
这一点,你是否已经注意到了呢?