I recently posted a question here about some memory issues I was having. I've got that fixed now thanks to this wonderful community but I'm facing another problem. I'm using Cocos2d to develop a game and I'm trying to remove a Sprite from and array. The problem arises when I try and release the temporary array I'm using to keep track of the sprites to remove.
NSMutableArray *spritesToRemove = [[NSMutableArray alloc] init];
// Loop through all sprites
for(CSSprite *sprite in _sprites){
if(sprite.toRemove){
[spritesToRemove addObject: sprite];
}
}
// loop through sprites to be removed
for(CSSprite *removeableSprite in spritesToRemove){
[_sprites removeObject: removeableSprite];
// Cocos2d code to remove a sprite
[self removeChild: removeableSprite cleanup: YES];
}
[spritesToRemove release]; // EXC_BAD_ACCESS error
I get a feeling the reason I'm getting the error is because I'm releasing the sprite object in [self removeChild: removeableSprite cleanup: YES]; before actually releasing the array. It all works fine if I remove the line [spritesToRemove release] but I obviously then have a memory leak on my hands.
I've tried moving the removal of the sprites around and I can get the memory thing sorted by completely omitting the line [self removeChild: removeableSprite cleanup: YES]; but then Cocos2d throws the same EXC_BAD_ACCESS error from within CCNode at [child visit]; of -(void) visit
Thanks again for your help :-)
EDIT: I enabled NSZombie and I got this message:
*** -[Sprite release]: message sent to deallocated instance 0xfa94cf0
Which to me kind of suggests my initial thought, somewhere an entry in the array is being released to soon. Would that be correct? If so is there anyway for me to find out where?
I managed to find the issue (and to be honest I feel a little silly :-P ). I was releasing the sprite manually once I added it to the array, which wasn't in the code provided so you guys couldn't have found it. The sprite was already set up to be autoreleased and thus was being cleared twice - causing my error.
Related
I thought I understood memory management well enough until this issue happened (Mac OS X 10.6): I have a custom NSView subclass with an NSMutableArray instance variable, but when I dealloc my view and attempt to release that instance variable, sometimes BOOM, EXC_BAD_ACCESS happens. This happens when I try to close my document window without quitting the program, but for some reason, even under identical conditions, sometimes it works without issue. Can anyone help me understand what's going on here? The relevant bits of code from my NSView subclass:
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
rainbow = [[NSMutableArray alloc] initWithObjects:
// some objects go here, followed by the nil sentinel
]
return self;
}
return nil;
}
And the dealloc method:
- (void)dealloc {
[super dealloc];
NSLog(#"Release the rainbow!");
if (rainbow) {
[rainbow removeAllObjects]; // EXC_BAD_ACCESS happens here
[rainbow release];
}
}
Even though I check whether rainbow is still around, sending it a message results in that segfault. There is one spot where it gets used: it's passed as a *info argument to a CGShading callback function. Here are the relevant bits of that function (which generally works without crashing):
NSMutableArray *colorStops = (NSMutableArray *)info;
[colorStops retain];
/*
...
*/
[colorStops release];
I'm guessing that there's something here about threads, but I really don't know. Anyone have any ideas? Thank you very much! I've reread the memory management guide; any more headdesking on this and my glass tabletop shatters in my face.
Always do
[super dealloc]
at the end of your dealloc method.
In addition to Terry's point about [super dealloc], the -removeAllObjects call will message all of the objects in the array (to release them). If you have overreleased any of those objects, the pointer that the array has may now point to deallocated or otherwise invalid space.
So, you have to review your memory management of all of the objects in the array. Run your app under the Zombies instrument. Do a Build and Analyze and resolve the identified issues.
i have this function :
-(void)blink:(CCSprite *)sprite
{
CCSprite *blinker=[sprite copy]; // i have add that to prevent sprite from change.
it gets a sprite and do animation on it, but sprite is keep changing all time cause its a pointer, so my function keep get a different sprites -WHICH I DONT WANT.
i was trying to copy it to another ccsprite, but its crashes.
whats wrong here ?
is that because i havnt release it ?
thanks alot
Can you post the code where call the blink method?
Maybe you can try this:
-(void) blink:(CCSprite*)sprite {
[sprite retain];
// Do some stuff with the sprite here
[sprite release];
}
However, functions should be called with thread-safe parameters so they don't get deallocated during the functions execution.
I have been working on iOS project, which used data downloading in background. Well, honestly say - implementation wasn't the best one (and was changed later to remove such nonobvious pattern), and I've got some problems with not-deallocated objects, and can't say, that docs gave me a clear understanding of what's going on. Code, demonstrating common idea:
- (void)loadModelAtIndex:(NSUInteger)index {
Model *model = [self modelAtIndex:index];
if (model) {
model.index = index;
[self performSelectorInBackgroundThread:#selector(loadModelInBackgroundThread) withObject:model]
}
}
- (void)loadModelInBackgroundThread:(Model *)model {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
model.data = [NSData dataWithContentsOfURL:model.url];
//and some other changes can be here
[self performSelectorOnMainThread:#selector(modelDidLoad) withObject:model waitUntilDone:NO]'
[pool drain];
}
- (void)modelDidLoad:(Model *)model {
[self saveModel:model atIndex:model.index];
[self loadModelAtIndex:model.index + 1];
}
Well, almost all time it has been working as expected. Except if caller has beed deallocated while downloading in background - Model object stayed in memory, without being released (I've got growing memory at this point).
Can anyone explain me, what will happen in case of deallocation, while background thread is running? I'm not sure this code can be suitable at all, but still interested. Any modification will make things run well?
Except if caller has beed deallocated while downloading in background - Model object stayed in memory
the performSelector | …Thread… calls retain their arguments. If your objects are deallocated in that time, the problem lies elsewhere. Run with zombies enabled to locate it - you can record every reference count.
I have a weird issue that comes up while releasing an object. In the object's dealloc method I am releasing another (sub) object. When this sub object is released I get an EXC_BAD_ACCESS error. I am pretty sure the sub-object is a valid pointer right before I call release on it.
This is how I've confirmed the weirdness - I set a break point inside the dealloc method, right before the release of the sub-object, and it is allocated! I can send it messages and it responds correctly. Another weird bug is that, if NSZombieEnabled is set to YES, I dont get an error at all, not even NSZombie's usual error that says I've sent a message to a deallocated object, the code runs properly.
Does anybody have any idea how to solve this?
Thanks in advance!
* EDIT 1 *
I've found out that if I place the [super dealloc] in the end of the dealloc, it crashes, but if I place it at start, it won't. I've had something similar before, only the other way - app crashed if I [super dealloc] in start of my dealloc, and didn't at the end.
Why does it make a difference and when should you use what?
* EDIT 2 *
Scratch Edit #1! It doesn't work either way.
Do your dealloc routine happen to look like this:
- (void)dealloc
{
[super dealloc];
[otherObject release];
}
if so, change the order. If you call [super dealloc], your object gets vanished from memory, trying to acces otherObject later will cause you to access non-allocated memory -> EXC_BAD_ACCESS.
Without seeing how the object is initialized, will be hard for anyone to know for sure.
MyObject *someObject = [MyObject alloc] init...]; // Make sure you aren't autoreleasing here
// do some stuff with someObject
[someObject release];
I created an NSScanner category method that shows a leak in instruments.
- (BOOL)scanBetweenPrefix:(NSString *)prefix
andSuffix:(NSString *)suffix
intoString:(NSString **)value
{
NSCharacterSet *charactersToBeSkipped = [self charactersToBeSkipped];
[self setCharactersToBeSkipped:nil];
BOOL result = NO;
// find the prefix; the scanString method below fails if you don't do this
if (![self scanUpToString:prefix intoString:nil])
{
MY_LOG(#"Prefix %# is missing.", prefix);
return result;
}
//scan the prefix and discard
[self scanString:prefix intoString:nil];
// scan the important part and save it
if ([self scanUpToString:suffix intoString:value]) // this line leaks
{
result = YES;
}
[self setCharactersToBeSkipped:charactersToBeSkipped];
return result;
}
I figure it's the way I'm passing the value to/from the method, but I'm not sure. It's a small leak (32 bytes), but I'd like to do this right if I can. Thanks in advance.
I found the answer. I had a model class that used the result of the code above, but forgot to release the property in the dealloc method. I should have caught it with Instruments, but I didn't know where to look. I got caught up in the call stack in the Extended Details, which had only part of the info I needed.
For morons like me, here's what I did:
Run your app with Instruments ... Leaks.
In Instruments, watch the leaked blocks view (the grid icon at the bottom) and turn on the extended detail.
If you have multiple leaked objects, click the disclosure triangle so you can look at a discrete address.
Next to an address, there will be a detail arrow. Click on it.
Now you'll be looking at history. It starts with Malloc and shows you each retain and release.
You should have a release for the malloc and a release for every retain in the history. Match up your retains and releases and look for the oddball. When in doubt, look toward the bottom of the page and carefully review any properties you may have forgotten to release in your dealloc method(s).
OK, I'm stupid. This method does not leak. The leak is farther down my call stack, but I can't figure out where. Sorry for the premature post. I'll follow up when I figure out where the real leak is, if I can't figure out the fix myself.
In answer to Yuji's comment, my method call is:
NSString *title;
[fileScanner scanBetweenPrefix:kTitlePrefix
andSuffix:kTitleSuffix
intoString:&title];
Is it the &title that's the problem? I patterned it after the existing NSScanner methods and calls.