遍历数组的同时删除元素引起崩溃的问题以及解决方案

遍历数组的同时删除元素引起崩溃的问题

一、问题

使用 for in遍历可变数组的同时删除元素会造成崩溃

崩溃日志为:

2018-03-10 19:40:43.970096+0800 ArrayRemoveObj[6500:611281] *** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x60400005a9a0> was mutated while being enumerated.'
*** First throw call stack:
(
    0   CoreFoundation                      0x0000000103cc012b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x0000000103354f41 objc_exception_throw + 48
    2   CoreFoundation                      0x0000000103d33b0c __NSFastEnumerationMutationHandler + 124
    3   ArrayRemoveObj                      0x0000000102a476a2 main + 834
    4   libdyld.dylib                       0x00000001076e8d81 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

所以 遍历可变数组时,最好不要做删除数组中元素的操作,因为删除操作可能会引起数组容量的变化,导致数组越界等问题。

二、分析

结合网上资料及实际代码对for in, enumerateObjectsUsingBlock:,for进行实际的分析。

声明如下数组:

NSMutableArray *array = [NSMutableArray arrayWithArray:@[@"aaa",@"bbb",@"ccc",@"ddd",@"eee"]];

使用 for in 必崩:

for (NSString *obj in array) {
    if ([obj isEqualToString:@"ddd"]) {
        [array removeObject:obj];
    }
}

使用 enumerateObjectsUsingBlock:未崩溃

[array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    if ([obj isEqualToString:@"ddd"]) {
        [array removeObject:obj];
    }
}];

使用 for未崩溃

for (int i = 0; i < array.count; i++) {
    NSString *obj = array[i];
    if ([obj isEqualToString:@"ddd"]) {
        [array removeObject:obj];
    }
}

记得会造成崩溃,有可能会造成数组越界。据说可能是苹果对此进行了保护,不推荐此法。

三、结论

从上面来看只for in会造成崩溃,但是结合以往经验来看最稳妥的方案,是对数组逆序遍历,然后再删除元素就没有问题了。

使用for in的逆序遍历删除元素:

for (NSString *obj in array.reverseObjectEnumerator) {
    if ([obj isEqualToString:@"ddd"]) {
        [array removeObject:obj];
    }
}

使用enumerateObjectsWithOptions:逆序遍历删除元素:

[array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    if ([obj isEqualToString:@"ddd"]) {
        [array removeObject:obj];
    }
}];

使用for逆序遍历删除元素:

for (int i = array.count - 1; i >= 0; i--) {
    NSString *obj = array[i];
    if ([obj isEqualToString:@"ddd"]) {
        [array removeObject:obj];
    }
}

最后提供一种容易理解的方法:把需要删除的元素放入新的数组中,遍历完成后,统一删除。这种在大量元素操作的时候,效率低下,推荐使用逆序遍历方式。

NSMutableArray *tempArray = nil;
for (NSString *obj in array) {
    if ([obj isEqualToString:@"ddd"]) {
        if (tempArray == nil) {
            tempArray = [NSMutableArray array];
        }
        [tempArray addObject:obj];
    }
}
if (tempArray && tempArray.count>0) {
    [array removeObjectsInArray:tempArray];
}

你可能感兴趣的:(遍历数组的同时删除元素引起崩溃的问题以及解决方案)