我们想要删除数组中的符合条件的元素时,经常对数组进行遍历,然后删除。但是这其中更确隐藏着很大的问题。如果当初能够仔细的分析一下,也不会导致今天的错误了。
比如我们有一个数组 CCArray *array;包含了value值分别为1~5的NSNumber对象,现在我们想删除其中value为1和2的两个对象,我们可能会这样操作:
1. for(NSNumber * number in array)
2. {
3. CCLOG(@"number:%@ ",[number description]);
4. if([number intValue]==2||[number intValue]==1) {
5. [array removeObject:number];
6. CCLOG(@"delete:%@ ",[number description]);
7. }
8. }
但这样操作,只会删除value为1的对象。因为删除这个对象后,array的count减少了,被删掉的对象后面的元素会向前移动,这样在这个for循环中就会漏掉1后面的那个对象,而且测试也发现,虽然循环中间删除了一个元素,但循环的次数并没有减少,只不过最后两次都是最后一个元素。
打印的值如下:
l number:0
l number:1
l delete:1
l number:3
l number:4
l number:4
如果我们使用如下的循环方式:
for (int i=0;i<[array count];i++) {
NSNumber *number = [array objectAtIndex:i];
CCLOG(@"number %d:%@ ",i,[number description]);
if ([number intValue]==2||[number intValue]==1) {
[array removeObject:number];
CCLOG(@"delete:%@ ",[number description]);
}
}
打印的值会变为:
l Number 0:0
l Number 1:1
l delete:1
l number 2:3
l number 3:4
可见同样的问题,在删除value为1的对象后,计数器增长为2,但此时数组中下标为2的元素已经变成了之前下标为3的对象,因为删除其中一个元素后,后面的所有元素都会向前移动,这个下标都会改变。这样变漏掉了一些元素。对于for in快速枚举,还会多次执行最后一个元素的遍历,这些都是导致问题的隐患。
为了避免以上的问题,我们可以使用倒序的方式删除:
int num = [array count];
for (int i=num-1;i>=0;i--) {
NSNumber *number = [array objectAtIndex:i];
CCLOG(@"number %d:%@ ",i,[number description]);
if ([number intValue]==2||[number intValue]==1) {
[array removeObjectAtIndex:i];
CCLOG(@"delete:%@ ",[number description]);
}
}
另外也可以通过创建临时数组的方式来解决,将所有要保留的对象加入另外一个数组,并完全删除原数组,或者将要删除的元素加入临时数组,然后执行[CCArray removeObjectInArray:];