views:

111

answers:

2

Hello.

I'm rather new to serious programming, so please forgive my dumbness, but I've failed to figure this out by myself.

I am writing a game with cocos2d-iphone. I have a scene object with several NSMutableSets in its ivars - units, buildings, rockets, etc. I have a problem removing objects from these NSMutableSets. When a bullet object is init-ed it among other things searches for a target that it should damage like this:

...
for ( RXUnit * unit in targetSet ) {
    if ( ccpDistance( unit.position, rayPoint ) < 20 ) {
    ... 
    [unit damageSelfBy:damage];
    ...
    }
}
...

The [unit damageSelfBy:damage] method kills the unit if it does not have enough HP to sustain damage. Among other things it does this:

[scene.units removeObject:self];

(scene.units is the same NSMutableSet as targetSet in the loop above)

Whenever this line is used, the bullet that was shot at the time the unit is being removed from the set fails to init properly. The bullet init method executes up to the for loop shown above (yes, the unit does get damaged and removed by previous bullet), but anything else that goes after this loop in the init method is just not executed.

Surprisingly, the application continues to run normally with no visible effects apart from that stuck bullet. Console does not show any warnings/errors, there are no warnings when compiling either.

I also tried delaying the time the unit is removed from the set by putting the actual removal code in the scene's update method, but that does not help.

I think I can make some ugly hacks to hide this problem, but I'm sure it will bite me in the ass later. And well, I would really like to understand what is going on and how to properly address it.

Any help/ideas are much appreciated. Maybe I shouldn't even store game objects in NSMutableSets?

+3  A: 

Is the set container the only reference to the object? I'm not sure that there is a defined behavior if you dealloc an object while you're running its methods. You may need to mark it as "dead" and then do a cleanup step afterward.

Seamus Campbell
So, I added another indirection and now a dying unit calls a method on scene object with this code:`[objectsToRemove addObject:[NSArray arrayWithObjects:aObject,aSet,nil]];`And then in `[scene update:(ccTime)dt]` the first lines are these: for ( NSArray * array in objectsToRemove ) { [[array objectAtIndex:1] removeObject:[array objectAtIndex:0]]; } [objectsToRemove removeAllObjects];`Still not sure why this particular trick works, but it solves the problem.PS sorry
raquo
Um, sorry, while I tried to figure out why code block formatting does not work the comment became uneditable. Thanks for help!
raquo
+2  A: 

If you remove the object from the set, and that set is the only reference to the object, then the object could be deallocated right then. Think for a second: the object whose methods you're running could disappear right now. That's Bad™.

That's why your delayed removal works; it's not a trick. You're waiting until you've finished using the object, then you're deleting it. There's a better way, though, than your objectsToRemove trick:

[self retain];
[self autorelease];
[scene.units removeObject:self];

autorelease will cause a delayed release message to be sent to your object when the run loop finishes, so by using retain, then autorelease, you prevent the object from disappearing the second it's removed from scene.units.

andyvn22
Sigh. Unfortunately does not work - bullets get stuck again. Frankly, I don't understand why this does not work and my weird indirection does. Maybe because mine gives more delay - around 1/45 sec... Thank you really, anyway!
raquo