我们在遍历可变数组时,最好不要做删除数组中元素的操作。
因为删除操作可能会引起数组容量的变化,导致数组越界等问题。
以前在使用for循环遍历的时候遇到过这个问题。
当时的做法是使用enumerateObjectsUsingBlock:,但是这次又遇到这个问题时,顺便好好的测试了一下for、for in、enumerateObjectsUsingBlock:。
实验结果如下:
NSMutableArray*array = [NSMutableArrayarrayWithArray:@[@"1",@"2",@"3",@"4",@"5"]];[array enumerateObjectsUsingBlock:^(id_Nonnull obj, NSUInteger idx,BOOL* _Nonnull stop) {if([obj isEqualToString:@"3"]) { [array removeObject:obj]; }}];NSLog(@"%@",array);// 输出结果:(1,2,4,5)
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
结果正常。
使用for in:
NSMutableArray *array= [NSMutableArray arrayWithArray:@[@"1",@"2",@"3",@"4",@"5"]];for(NSString *objinarray) {if([obj isEqualToString:@"3"]) { [arrayremoveObject:obj]; }}NSLog(@"%@",array);
1
2
3
4
5
6
7
1
2
3
4
5
6
7
结果出现崩溃,信息如下:
2016-11-1122:48:06.886Solutions[15297:2231002] *** Terminating app duetouncaughtexception'NSGenericException', reason:'*** Collection <__NSArrayM: 0x1001060b0> was mutated while being enumerated.'*** First throw call stack:(0CoreFoundation0x00007fff929c14f2__exceptionPreprocess +1781libobjc.A.dylib0x00007fff8fcb2f7eobjc_exception_throw +482CoreFoundation0x00007fff92a2815c__NSFastEnumerationMutationHandler +3003Solutions0x0000000100000d1cmain +5084libdyld.dylib0x00007fff90cd05adstart +1)libc++abi.dylib: terminatingwithuncaughtexceptionoftypeNSException
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
使用for:
NSMutableArray *array= [NSMutableArray arrayWithArray:@[@"1",@"2",@"3",@"4",@"5"]];for(inti =0; i
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
结果正常,可能是苹果对普通的for循环内部的处理做了修正,以前这样写是会报错的。
其实,最正确与稳妥的方案,是对数组逆序遍历,然后再删除元素就没有问题了。
例如for in的逆序遍历:
NSMutableArray *array= [NSMutableArray arrayWithArray:@[@"1",@"2",@"3",@"4",@"5"]];for(NSString *obj inarray.reverseObjectEnumerator) {if([obj isEqualToString:@"3"]) { [arrayremoveObject:obj]; }}NSLog(@"%@",array);// 输出结果:(1,2,4,5)
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
使用
NSMutableArray*array = [NSMutableArrayarrayWithArray:@[@"1",@"2",@"3",@"4",@"5"]];[array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id_Nonnull obj, NSUInteger idx,BOOL* _Nonnull stop) {if([obj isEqualToString:@"3"]) { [array removeObject:obj]; } }];NSLog(@"%@",array);// 输出结果:(1,2,4,5)
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
关于for 的逆序
NSMutableArray *array= [NSMutableArray arrayWithArray:@[@"1",@"2",@"3",@"4",@"5"]];for(inti =array.count -1; i >=0; i--) { NSString *obj =array[i];if([obj isEqualToString:@"3"]) { [arrayremoveObject:obj]; }}NSLog(@"%@",array);// 输出结果:(1,2,4,5)
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
以前做Java开发的时候,也遇到过这种遍历删除元素的,那时候的做法是在遍历,删除元素后做一次i–操作。
现在想来,也可以逆序遍历然后直接删除即可。
====================================
或者楼主可以用 while 循环语句来做。
例如:
while(array.count>0)
{
obj = [array objectAtIndex:0];
//判断处理
…
//remove
[array removeObjectAtIndex:0];
}
=========================================
可以用NSPredicate来完成:
NSArray *sourceArray = @[];
NSArray *whatYouNeedArray = [sourceArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return ![evaluatedObject shouldBeDeleted];
}]];
whatYouNeedArray就是你要的array,已经去除掉该删除的项。
=======================================
一般不要这样做,如果要删除,一定要在删除之后,break.
Oc语言中有三种遍历数组的方式,一是传统的for循环,二是for-in循环,三是迭代器。其中,第一种方式的效率最低。第二种遍历方法如下,第三种使用比较少,你可以自己去网上看下。
for (NSString * str in names)
{
if ([str isEqualTo: @"something"]){
[names removeObject: str];
break;//一定要有break,否则会出错的。
}
}