3

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?

Jeff Wang
  • 183
  • 2
  • 11
  • 1
    Using common sense, it'd be pretty easy to come up with the conclusion that you can't edit objects/arrays during an enumeration. Not too sure why though. – TheAmateurProgrammer Oct 18 '12 at 08:19
  • [Best way to remove from NSMutableArray while iterating?](http://stackoverflow.com/questions/111866/best-way-to-remove-from-nsmutablearray-while-iterating) – Parag Bafna Oct 18 '12 at 08:29
  • if i shouldn't remove array while iterating , why my second example forloop for "forRemoving" doesn't crash? – Jeff Wang Oct 19 '12 at 01:37

4 Answers4

6

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];
    }
}
rckoenes
  • 69,092
  • 8
  • 134
  • 166
  • 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 Jul 30 '18 at 11:58
  • @rckoenes you are genius my friend randomly looking for solutions and found your answer. How can I forget this basic rule!!! – Mihir Oza Nov 02 '20 at 13:41
3

Like rckoenes said, you break the enumaration by removing stuff in the array while iterating through it.

What you can do is to have a second array where you insert the objects that you want to remove. Then, after your enumeration is finished you can remove all the objects that are found in the second array, from your first array.

Mikael
  • 3,572
  • 1
  • 30
  • 43
1

You must not modify a collection while iterating through its items.

If you iterate index based (i.e. the classical for loop), you can remove things, but be careful about adjusting your index.

Eiko
  • 25,601
  • 15
  • 56
  • 71
1

Check this link

https://developer.apple.com/library/mac/documentation/cocoa/conceptual/Collections/Articles/Enumerators.html

Enumeration is “safe”—the enumerator has a mutation guard so that if you attempt to modify the collection during enumeration, an exception is raised.

Community
  • 1
  • 1
brianLikeApple
  • 4,262
  • 1
  • 27
  • 46