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

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

作者: 演员新之助 | 来源:发表于2018-05-23 16:09 被阅读129次

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

    一、问题

    使用 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];
    }
    

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/ywvrjftx.html