NSMutableArray 问题的快速枚举

Fast Enumeration over NSMutableArray problems

我在思考为什么我们不能在枚举期间改变集合时遇到一些问题...显然,如果您正在进行任何类型的快速枚举,如果您尝试这样做,系统应该会抛出异常变异。下面是我在枚举期间发生变异的三个示例。一个是简单的 C 风格循环,另外两个使用某种形式的快速枚举。我只收到针对案例 2 抛出的枚举异常。难道我不应该也收到针对案例 1 抛出的异常吗?为什么案例 1 有效?此外,整个堆栈溢出的人都说我的案例 3 是不好的做法,但为什么呢?它很简单而且似乎有效。两个不同的快速枚举循环的行为方式的不一致以及对 C 风格循环的普遍厌恶正在打乱我对这里的理解。而不是模糊的通用经验法则,如果有人真的能把它分解成一门科学,那真的会有帮助。从根本上讲,我想知道为什么这里的例外情况不一致,以及为什么案例 3 在显然 "shouldn't" 或 "bad practice."

时对我有用
//Case 1:
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"phuj", @"whub", @"adgh", nil];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [array removeObjectAtIndex:idx];
}];

//Case 2:
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"phuj", @"whub", @"adgh", nil];
for (NSString *string in array) {
    [array removeObject:string];
}

//Case 3:
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"phuj", @"whub", @"adgh", nil];
for (int i = 0; i < [array count]; i++) {
    [array removeObjectAtIndex:i];
}

我们喜欢简单明了的代码。

我不会谈论情况 2,因为它会抛出一个异常并且您不能改变其中的数组。

我想从一个例子开始,假设我们要从可变数组中删除 "whub" 项,并且数组的内容是 "phuj, whub, whub, adgh",如您所见,它有两个 whub。

让我们开始使用 C 风格循环编写代码:

for (int i = 0; i < [mArray1 count]; i++) {
    NSString *str = mArray1[i] ;
    if ([str isEqualToString:@"whub"]) {
        [mArray1 removeObjectAtIndex:i] ;
    }
}

代码有bug,去掉第一个"whub"后,所有位于它后面的item索引都会减1,所以第二个whub的索引应该是1,i目前为 1。在下一个循环中,i 是 2,因此它会跳过第二个 whub。您可以更改代码以使其正确。

for (int i = 0; i < [mArray1 count]; i++) {
    NSString *str = mArray1[i] ;
    if ([str isEqualToString:@"whub"]) {
        [mArray1 removeObjectAtIndex:i] ;
        --i ;
    }
}

有效,但并不简单,我们修改了索引i,这使代码变得复杂。

让我们尝试像案例 1 一样使用 enumerateObjectsUsingBlock 方法编写代码:

NSMutableArray *array = [NSMutableArray arrayWithObjects:@"phuj", @"whub", @"whub", @"adgh", nil] ;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    if ([obj isEqualToString:@"whub"]) {
        [array removeObjectAtIndex:idx] ;
    }
}];

如果你构建并 运行 它,你会发现结果数组仍然包含一个 "whub" 而这一次,我们无法修改索引来修复它。

结论:你说了很多你可以在循环中改变数组,你可以改变数组的事实并不意味着你会得到正确的答案。事实上,使用 c 风格的循环你可以做任何你想做的事,它不会崩溃,但我们说它不好是因为我们有更好的方法。在实践中,我们希望用简单的代码来实现目标,让别人容易理解,自己也能从中受益。

我想用这种方式,我觉得简单明了

NSMutableArray *arrayRemove = [NSMutableArray array] ;
for (NSString *str in mArray1) {
    if ([str isEqualToString:@"whub"]) {
        [arrayRemove addObject:str] ;
    }
}
[mArray1 removeObjectsInArray:arrayRemove] ;