Reputation: 55
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.
Upvotes: 0
Views: 2942
Reputation: 104698
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];
Upvotes: 2
Reputation: 622
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--;
}
Upvotes: 1