可变数组使用快速枚举删除元素删除不尽的问题

最近开发过程中,发现使用 enumerateObjectsUsingBlock 删除元素时发现有删除不尽的问题

删除array中XPZ_EnumPerson对象name为2的元素

利用 enumerateObjectsUsingBlock 正序的方法

1.name为2的两个对象相邻

    XPZ_EnumPerson *person1 = [[XPZ_EnumPerson alloc] init];
    person1.name = @"1";
    XPZ_EnumPerson *person2 = [[XPZ_EnumPerson alloc] init];
    person2.name = @"2";
    XPZ_EnumPerson *person3 = [[XPZ_EnumPerson alloc] init];
    person3.name = @"2";
    XPZ_EnumPerson *person4 = [[XPZ_EnumPerson alloc] init];
    person4.name = @"3";
    XPZ_EnumPerson *person5 = [[XPZ_EnumPerson alloc] init];
    person5.name = @"4";
    XPZ_EnumPerson *person6 = [[XPZ_EnumPerson alloc] init];
    person6.name = @"5";
    XPZ_EnumPerson *person7 = [[XPZ_EnumPerson alloc] init];
    person7.name = @"6";
    XPZ_EnumPerson *person8 = [[XPZ_EnumPerson alloc] init];
    person8.name = @"7";
    
    NSMutableArray *array = [NSMutableArray array];
    [array addObject:person1];
    [array addObject:person2];
    [array addObject:person3];
    [array addObject:person4];
    [array addObject:person5];
    [array addObject:person6];
    [array addObject:person7];
    [array addObject:person8];
    
    [array enumerateObjectsUsingBlock:^(XPZ_EnumPerson *person, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"对象:%@--下标:%lu",person.name, (unsigned long)idx);
        if ([person.name isEqualToString:@"2"]) {
            [array removeObject:person];
//            [array removeObjectAtIndex:idx];
        }
    }];
    for (XPZ_EnumPerson *person in array) {
        NSLog(@"%@",person.name);
    }
 /*
     [控制器:XPZ_enumTestVC.m -- line: 133行]    对象:1--下标:0
     [控制器:XPZ_enumTestVC.m -- line: 133行]    对象:2--下标:1
     [控制器:XPZ_enumTestVC.m -- line: 133行]    对象:3--下标:2
     [控制器:XPZ_enumTestVC.m -- line: 133行]    对象:4--下标:3
     [控制器:XPZ_enumTestVC.m -- line: 133行]    对象:5--下标:4
     [控制器:XPZ_enumTestVC.m -- line: 133行]    对象:6--下标:5
     [控制器:XPZ_enumTestVC.m -- line: 133行]    对象:7--下标:6
     [控制器:XPZ_enumTestVC.m -- line: 139行]    1
     [控制器:XPZ_enumTestVC.m -- line: 139行]    2
     [控制器:XPZ_enumTestVC.m -- line: 139行]    3
     [控制器:XPZ_enumTestVC.m -- line: 139行]    4
     [控制器:XPZ_enumTestVC.m -- line: 139行]    5
     [控制器:XPZ_enumTestVC.m -- line: 139行]    6
     [控制器:XPZ_enumTestVC.m -- line: 139行]    7
     */

2.name为2的对象不相邻

    XPZ_EnumPerson *person1 = [[XPZ_EnumPerson alloc] init];
    person1.name = @"1";
    XPZ_EnumPerson *person2 = [[XPZ_EnumPerson alloc] init];
    person2.name = @"2";
    XPZ_EnumPerson *person3 = [[XPZ_EnumPerson alloc] init];
    person3.name = @"3";
    XPZ_EnumPerson *person4 = [[XPZ_EnumPerson alloc] init];
    person4.name = @"2";
    XPZ_EnumPerson *person5 = [[XPZ_EnumPerson alloc] init];
    person5.name = @"4";
    XPZ_EnumPerson *person6 = [[XPZ_EnumPerson alloc] init];
    person6.name = @"5";
    XPZ_EnumPerson *person7 = [[XPZ_EnumPerson alloc] init];
    person7.name = @"6";
    XPZ_EnumPerson *person8 = [[XPZ_EnumPerson alloc] init];
    person8.name = @"7";
    
    NSMutableArray *array = [NSMutableArray array];
    [array addObject:person1];
    [array addObject:person2];
    [array addObject:person3];
    [array addObject:person4];
    [array addObject:person5];
    [array addObject:person6];
    [array addObject:person7];
    [array addObject:person8];
    
    [array enumerateObjectsUsingBlock:^(XPZ_EnumPerson *person, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"对象:%@--下标:%lu",person.name, (unsigned long)idx);
        if ([person.name isEqualToString:@"2"]) {
            [array removeObject:person];
        }
    }];
    for (XPZ_EnumPerson *person in array) {
        NSLog(@"%@",person.name);
    }
    /*
     [控制器:XPZ_enumTestVC.m -- line: 172行]    对象:1--下标:0
     [控制器:XPZ_enumTestVC.m -- line: 172行]    对象:2--下标:1
     [控制器:XPZ_enumTestVC.m -- line: 172行]    对象:2--下标:2
     [控制器:XPZ_enumTestVC.m -- line: 172行]    对象:5--下标:3
     [控制器:XPZ_enumTestVC.m -- line: 172行]    对象:6--下标:4
     [控制器:XPZ_enumTestVC.m -- line: 172行]    对象:7--下标:5
     [控制器:XPZ_enumTestVC.m -- line: 178行]    1
     [控制器:XPZ_enumTestVC.m -- line: 178行]    3
     [控制器:XPZ_enumTestVC.m -- line: 178行]    4
     [控制器:XPZ_enumTestVC.m -- line: 178行]    5
     [控制器:XPZ_enumTestVC.m -- line: 178行]    6
     [控制器:XPZ_enumTestVC.m -- line: 178行]    7
     */

利用 enumerateObjectsWithOptions:方式

    XPZ_EnumPerson *person1 = [[XPZ_EnumPerson alloc] init];
    person1.name = @"1";
    XPZ_EnumPerson *person2 = [[XPZ_EnumPerson alloc] init];
    person2.name = @"2";
    XPZ_EnumPerson *person3 = [[XPZ_EnumPerson alloc] init];
    person3.name = @"2";
    XPZ_EnumPerson *person4 = [[XPZ_EnumPerson alloc] init];
    person4.name = @"3";
    XPZ_EnumPerson *person5 = [[XPZ_EnumPerson alloc] init];
    person5.name = @"4";
    XPZ_EnumPerson *person6 = [[XPZ_EnumPerson alloc] init];
    person6.name = @"5";
    XPZ_EnumPerson *person7 = [[XPZ_EnumPerson alloc] init];
    person7.name = @"6";
    XPZ_EnumPerson *person8 = [[XPZ_EnumPerson alloc] init];
    person8.name = @"7";
    
    NSMutableArray *array = [NSMutableArray array];
    [array addObject:person1];
    [array addObject:person2];
    [array addObject:person3];
    [array addObject:person4];
    [array addObject:person5];
    [array addObject:person6];
    [array addObject:person7];
    [array addObject:person8];
    
    [array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(XPZ_EnumPerson *person, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"对象:%@--下标:%lu",person.name, (unsigned long)idx);
        if ([person.name isEqualToString:@"2"]) {
            [array removeObjectAtIndex:idx];
        }
    }];
    for (XPZ_EnumPerson *person in array) {
        NSLog(@"%@",person.name);
    }
    /*
     [控制器:XPZ_enumTestVC.m -- line: 306行]    对象:7--下标:7
     [控制器:XPZ_enumTestVC.m -- line: 306行]    对象:6--下标:6
     [控制器:XPZ_enumTestVC.m -- line: 306行]    对象:5--下标:5
     [控制器:XPZ_enumTestVC.m -- line: 306行]    对象:4--下标:4
     [控制器:XPZ_enumTestVC.m -- line: 306行]    对象:3--下标:3
     [控制器:XPZ_enumTestVC.m -- line: 306行]    对象:2--下标:2
     [控制器:XPZ_enumTestVC.m -- line: 306行]    对象:2--下标:1
     [控制器:XPZ_enumTestVC.m -- line: 306行]    对象:1--下标:0
     [控制器:XPZ_enumTestVC.m -- line: 312行]    1
     [控制器:XPZ_enumTestVC.m -- line: 312行]    3
     [控制器:XPZ_enumTestVC.m -- line: 312行]    4
     [控制器:XPZ_enumTestVC.m -- line: 312行]    5
     [控制器:XPZ_enumTestVC.m -- line: 312行]    6
     [控制器:XPZ_enumTestVC.m -- line: 312行]    7
     */

总结

  1. 从上面的例子中可以看出,当使用快速枚举 enumerateObjectsUsingBlock 方法删除数组元素的时候,如果有条件相同的相邻对象,例如例子中的 person2.name = @"2";person3.name = @"2"; 两个相邻的对象无论是用下标 [array removeObjectAtIndex:idx] 或者是 [array removeObject:person] 进行删除操作,都会出现遍历不完全的情况。

  2. 利用 enumerateObjectsWithOptions: 方法使用倒序的方式遍历数组可以遍历完全。

    NSEnumerationReverse 为倒序

    [array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(XPZ_EnumPerson *person, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"对象:%@--下标:%lu",person.name, (unsigned long)idx);
        if ([person.name isEqualToString:@"2"]) {
            [array removeObjectAtIndex:idx];
        }
    }];
    
  3. 出现遍历不完全的原因:
    当发现符合删除条件的时候将该元素从数组里删除,这是数组里的元素会向前移动,各个元素的下标会-1,但是遍历过程是按idx递增的,所以下一个获取的元素是跳过了一个下标的元素,也就是删除一个再遍历获取到的元素实际上是原始数组跳过一个下标的元素,所以删除用 enumerateObjectsUsingBlock 该方式删除不尽要删除的元素
    例如:
    将下标为1的对象删除,下次遍历,就会越过下标为2的对象遍历下标为3的对象,因为此时下标为3的对象在删除成功后的数组中-1后下标变为2了。

  4. 所以正确的方式是要采用倒序方式, 下一次遍历下标不会发生变化

你可能感兴趣的:(可变数组使用快速枚举删除元素删除不尽的问题)