objective-cnsmutablearrayfast-enumeration

fast enumeration for removing item in NSMutableArray crash


i have a strange issue , if i remove my item at forin enumeration , it would crash , so like this:

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

if i change my code to

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];

this would work perfect , could someone tell me why?


Solution

  • The answer is that if you remove an object the other objects in that array move postion in the array since an item is removed.

    For example we have an array with 4 items, if we remove the first item (item 0) the item that used to be at index 1 is now at index 0 and the item at 2 is now at 1. Thus the enumeration breaks.

    You could solve this by looping thru the array from the count down to 0:

    for (int i = [array count]-1; i >= 0; i--) {
        id object = [array objectAtIndex:i];
    
        if (some check) {
           [array removeObjectAtIndex:i];
        }
    }