0

In my iphone game, when run on the ios 4.0 simulator everything works fine. However when running with the 5.0 simulator or on a 5.0+ device, the app crashes on the first level leaving an error:

Terminating app due to uncaught exception 'NSGenericException', reason:
*** Collection <__NSArrayM: 0x1416eea0> was mutated while being enumerated.
*** First throw call stack:
(0x1d6a052 0x20d0d0a 0x1d69c21 0x6f8e 0x8bd48 0x94020 0xba169 0xbcee4 0x85a2db 0x85a1af 0x1d3e966 0x1d3e407 0x1ca17c0 0x1ca0db4 0x1ca0ccb 0x2702879 0x270293e 0x90fa9b 0x1f31 0x1eb5 0x1)
terminate called throwing an exception(lldb)

I've think i've narrowed the problem down to this piece of code. I understand that the problem is removing objects within the for-loop but cannot seem to figure out a solution.

Here is my code:

//remove the projectile

for (CCSprite *projectile in projectilesToDelete) {
    [_projectiles removeObject:projectile];
    [self removeChild:projectile cleanup:YES];
}       
[projectilesToDelete release];

//remove the projectile

for (CCSprite *targetDel in targetsToDelete) {  
    targetDel.position = ccp(-2000, -2000);
    [self removeChild:targetDel cleanup:YES];
    [_targets removeObject:targetDel];
}                        
[targetsToDelete release];

Please help, been trying to figure out for the past few days.

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
sunnyssrcp
  • 55
  • 3
  • 7
  • 1
    Where do the `projectilesToDelete` and `targetsToDelete` arrays come from? – Lily Ballard Aug 27 '12 at 05:52
  • is `_projectiles` the same collection as `projectilesToDelete`? – andrewdotnich Aug 27 '12 at 05:52
  • @KevinBallard I declared the _targets and _projectiles in my .h file like this NSMutableArray *_targets; and NSMutableArray *_projectiles; and the ...ToDelete comes from the same .m file. I declared NSMutableArray *projectilesToDelete = [[NSMutableArray alloc] init]; NSMutableArray *targetsToDelete = [[NSMutableArray alloc] init]; at the top of the function. – sunnyssrcp Aug 27 '12 at 06:03
  • as you do not access your `targetsToDelete` and `projectilesToDelete` arrays within the loops i suggest a closer look at your `removeChild` method. Seems like you have another loop in this method which removes elements from an Array durring iteration. – Jonathan Cichon Aug 27 '12 at 06:12
  • anyway to send the entire project to you. Cant seem to figure this out. Again this only happens when I run the project on the 5.0 simulator or device. Any previous version works fine. – sunnyssrcp Aug 27 '12 at 06:20
  • @sunnyssrcp [Set an exception breakpoint](http://stackoverflow.com/questions/4961770/run-stop-on-objective-c-exception-in-xcode-4). Then run your app again and make it crash. Copy the stack trace in Xcode and paste it into your question. Edit your question to paste it in. Don't try to put it in a comment. – rob mayoff Aug 27 '12 at 06:47
  • When the mutated while being enumerated error goes away the game freezes at the same point and a new error, child not found is display. I'll be glad to email somebody the entire project if they're willing to get it to run on the 5.0+ simulator. – sunnyssrcp Aug 27 '12 at 07:42

2 Answers2

2

In short, the expression:

for (OBJ * VAR in COLLECTION) {

uses a technique called Fast Enumeration. What happens here is the compiler inserts some hidden backing storage on the stack and requests a collection of elements from a type which may be enumerated. Because it grabs many objects at once and iterates over them, it is an error to mutate the container (COLLECTION) while it is being enumerated because the stack area and the collection can fall out of sync.

The workaround is to avoid fast enumeration when you mutate what you are enumerating -- either that, or you could enumerate a copy in some cases. The standard for(i;c;e) loop does not use fast iteration -- but the for(in) does use it.


One way to alter the program to avoid fast enumeration errors is:

// remove the projectile
while (projectilesToDelete.count) {
    CCSprite * projectile = projectilesToDelete[0];
    [_projectiles removeObject:projectile];
    [self removeChild:projectile cleanup:YES];
}
[projectilesToDelete release];

// remove the projectile
while (targetsToDelete.count) {
    CCSprite * targetDel = targetsToDelete[0];
    targetDel.position = ccp(-2000, -2000);
    [self removeChild:targetDel cleanup:YES];
    [_targets removeObject:targetDel];
}
[targetsToDelete release];
justin
  • 104,054
  • 14
  • 179
  • 226
  • When the mutated while being enumerated error goes away the game freezes at the same point and a new error, child not found is display. I'll be glad to email somebody the entire project if they're willing to get it to run on the 5.0+ simulator. – sunnyssrcp Aug 27 '12 at 07:41
1

Problem with your code is that, consider this..... projectilesToDelete array has 3 elements(A, B, C)...

In first iteration, you removeObject(A).. at that array count becomes 2 i.e., array content will be (B, C) but it considers it to be 3(B,C, Nil).

So Array got modified while enumerating. So it will give an exception.

change the code to this

// get array length
int count = projectilesToDelete. length;

// iterate through the array    
for (int iter = 0; iter < count; inter++) 
{
     CCSprite *projectile = projectilesToDelete [iter];

     [_projectiles removeObject:projectile];

     [self removeChild:projectile cleanup:YES];

      // decrement count
      count--;
      iter--;
} 
Ashwin Kumar
  • 622
  • 4
  • 7
  • should I create the same for targetsToDelete? – sunnyssrcp Aug 27 '12 at 06:35
  • and using this way would the [projectilesToDelete release]; still remain – sunnyssrcp Aug 27 '12 at 06:43
  • yea whenever you modify array... you need to do this. No need to do when replacing objects in an array, release can be done – Ashwin Kumar Aug 27 '12 at 07:10
  • When the mutated while being enumerated error goes away the game freezes at the same point and a new error, child not found is display. I'll be glad to email somebody the entire project if they're willing to get it to run on the 5.0+ simulator. – sunnyssrcp Aug 27 '12 at 07:42
  • first remove chilf then remove from array – Ashwin Kumar Aug 27 '12 at 10:50
  • the problem is that I cant exactly find where the array is being mutated. The project has about 100 source files. Any way to track the exact line or which NSMutableArray is causing the problem. Xcode gives some unreadable array NSArrayM: 0x1416eea0. Thank you for helping by the way! – sunnyssrcp Aug 28 '12 at 05:45
  • make sure that you dont change array by removing objects and all when enumerated. MutableArray is array which is mutated i.e., that can be changed – Ashwin Kumar Aug 28 '12 at 09:04