在NSMutableArray中使用快速枚举删除元素会导致崩溃

3

我有一个奇怪的问题,如果我在forin枚举时删除我的项目,它会崩溃,像这样:

for (Obstacle *obstacleToTrack in _obstaclesToAnimate) {
    //this if else not so important for happening crash
    if(obstacleToTrack.distance > 0){
        obstacleToTrack.distance -= _playerSpeed * _elapsed;
    }else{
        if (obstacleToTrack.watchOut) {
            obstacleToTrack.watchOut = NO;
        }
        obstacleToTrack.x -= (_playerSpeed + obstacleToTrack.speed) * _elapsed;
    }
    if (obstacleToTrack.x < -obstacleToTrack.width || _gameState == GS_OVER) {
        [self removeChild:obstacleToTrack];
        //this line makes crash happen , if remove this line code work fine
        [_obstaclesToAnimate removeObject:obstacleToTrack];
    }
}

如果我将代码更改为

NSMutableArray *forRemoving = [[NSMutableArray alloc]init];
for (Obstacle *obstacleToTrack in _obstaclesToAnimate) {
    //this if else not so important for happening crash
    if(obstacleToTrack.distance > 0){
        obstacleToTrack.distance -= _playerSpeed * _elapsed;
    }else{
        if (obstacleToTrack.watchOut) {
            obstacleToTrack.watchOut = NO;
        }
        obstacleToTrack.x -= (_playerSpeed + obstacleToTrack.speed) * _elapsed;
    }
    if (obstacleToTrack.x < -obstacleToTrack.width || _gameState == GS_OVER) {
        // code change here
        [self removeChild:obstacleToTrack];
        [forRemoving addObject:obstacleToTrack];
    }
}
for(Obstacle *obstacleToTrack in forRemoving){
    [_obstaclesToAnimate removeObject:obstacleToTrack];
    [forRemoving removeObject:obstacleToTrack];
}
[forRemoving release];

这会完美地运作,有人能告诉我为什么吗?

1
使用常识,很容易得出这样的结论:在枚举过程中不能编辑对象/数组。不太确定为什么会这样。 - TheAmateurProgrammer
在迭代NSMutableArray时最好的删除方式是什么? - Parag Bafna
如果我在迭代时不应该删除数组,那么为什么我的第二个示例中针对“forRemoving”的for循环没有崩溃? - Jeff Wang
4个回答

6
答案是,如果您删除一个对象,则该数组中的其他对象会移动位置,因为已删除一个项。例如,我们有一个包含4个项目的数组,如果我们删除第一个项目(项目0),原来在索引1处的项目现在在索引0处,而在2处的项目现在在1处。这样枚举就会中断。您可以通过从计数向下循环遍历数组来解决这个问题:
for (int i = [array count]-1; i >= 0; i--) {
    id object = [array objectAtIndex:i];

    if (some check) {
       [array removeObjectAtIndex:i];
    }
}

2
[array count] returns the number of items in the array, but the maximum index that can be passed to [array objectAtIndex:] is [array count]-1 so this crashes on the first pass. The correct code would be: for (int i = [array count]-1; i >= 0; i--) { ... - Jason Crocker
@rckoenes,你真是个天才啊,我的朋友!我随机寻找解决方案,却发现了你的答案。我怎么能忘记这个基本规则!!! - Mihir Oza

3

正如rckoenes所说,当你在遍历数组时删除其中的内容,就会中断枚举。

你可以创建第二个数组,将想要删除的对象插入其中。然后,在枚举完成后,从第一个数组中删除所有在第二个数组中找到的对象。


1

在遍历集合项时,您不能修改集合本身。

如果使用索引进行迭代(即经典的for循环),则可以删除元素,但要注意调整索引。


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接