Scenes not completly released in Cocos2d iPhone application - debugging - iphone

I have an iPhone application in Cocos2d that sometimes crashes on actual device, due to memory problems.
What I have so far found out is that the scenes, when switched, aren't fully released - the [retainCount] for them is somewhat about 4-10 :)
The dealloc method never gets called, and then I assume, when I switch the scenes a few times, the memory issue shows up.
I'm wondering - where should I relese the scene? Since it has number of children, I suppose I should be doing a cleanup-removal of them. But it turns out that removing all children from the layer doesn't decrease the retain count of it. I added such a piece of code to my cleanup method:
- (void) cleanup {
while ([self.children count] > 0) {
CCLOG(#"child: %d - %# rc: %d", 0, [self.children objectAtIndex:0], [[self.children objectAtIndex:0] retainCount]);
[self removeChild:[self.children objectAtIndex:0] cleanup:YES];
}
[super cleanup];
}
But then the [self retainCount] method still returns a number greater then 1 or 0, and my dealloc doesn't get called.
Is there something I should be doing in order to release those children properly? If I add my own subclass of CCSprite as child, should I do something specific in that class' release or dealloc method, other then just calling it's [super] method?

Do not call retainCount
retainCount is useless, as you've discovered, when dealing with complex frameworks. There are any number of internal implementation details that could cause the retain count to be an unexpected value at any given time without indicating a bug.
You should release the scene to balance however many times you retained the scene, no more and no less.
If you release it more times than you retained it, you're app will likely crash whenever you [potentially accidentally] fix the real problem.
In general, when dealing with a hierarchy of items like views, layers, or sprites, you remove the root view/layer/sprite and that removal takes care of tearing down the hierarchy (including releasing as needed).
That assumes that you haven't retained anything in the hierarchy. If you have, then you need to also release those references when the root is removed and released.

Usually you don't have to release your children by yourself. How do you add your child?

Related

Iphone App crashes when releasing view controller containing scrolling UITableView

This is a very very... very odd bug. It's hard to describe the exact project I have, but I will try and create a simpler representation of the classes I have. It goes like this:
Assume I have a navigation controller as my top view controller. Inside it, at one moment in time I have a UIViewController, let's say a ContactsScreenController. The view for this contains multiple UITableView that each is controlled by a separate object of type MyTableController (delegate&datasource). I do this by keeping an array of controllers
// This is the interface for my screen controller. An object of this type goes in a top-
// level navigation controller
// MainScreenController.h
#interface ContactsScreenController : UIViewController
NSMutableArray* tableControllers;
#end
// MainScreenController.m
- (UITableViewCell*)cellForRowAtIndexPath..something..
{
// Here what I do is create a new controller if needed, and add it to tableControllers
// Memory allocations & releases are good because I checked with instruments
}
#define SAFE_DEL(x) { if (x != nil) { [x release]; x = nil; } }
- (void)dealloc
{
SAFE_DEL(tableControllers);
[super dealloc];
}
Now, MyTableController is a more complicated object as it handles fetching data from a web service, but basically what I do is I want to make sure that when the object is deleted, I cancel any pending data requests, like this:
// MyTableController.m
- (void)dealloc
{
[globalDataProvider cancelRequestsForController:self];
// release other objects i might have
[super dealloc];
}
OK, so this is my objects setup. The crash occurs when I am deleting the object tableControllers. It decrements the retainCount for my MyTableController objects and it reaches 0 (checked using Instruments). But for some UNKNOWN reason, I get calls for cancelRequestsForController, AFTER the retain count has been zero.
Obviously, I get a EXC_BAD_ACCESS.
Before you start thinking it's a problem with my retain/release pairs, the application runs perfectly if I am releasing the main screen controller while the inner tables are static. As soon as the are scrolling and I hit the Back button in the navigation controller I experience the bug.
I've checked using instruments the entire history of retain count changes for my inner controllers and it is good (no unusual stuff). When the bug occurs, my last entry in the history is from QuartzCore run_animation_callbacks with a retain count of -1.
Any ideas? :)
PS: As a quick solution to get the project going, I've moved the cancelRequestsForController in a separate method and I'm manually calling it for each object in tableControllers before the release. This way I am sure that there will be no calls after the release, no matter the state of the tableview.
- (void)dealloc
{
for (TableController* c in tableControllers)
[c cancelRequests];
SAFE_DEL(tableControllers);
[super dealloc];
}
But I don't like this solution for several reasons.
Thanks to everybody for the answers/comments.
The problem was generated by a call to [super dealloc] in an object, before I got the chance to release my objects. This caused a lot of crazy stuff to happen. I moved the [super dealloc] at the end of my dealloc method (after my releases) and it works fine now.
The SAFE_DEL macro is unnecessary and makes the code less readable. Simply do:
[someObject release], someObject = nil;
It won't matter if someObject is already nil and it makes the code more directly readable.
As soon as the are scrolling and I hit
the Back button in the navigation
controller I experience the bug.
Any time you have non-memory management logic, you have fragility. Namely, when dealloc is being executed, it is quite likely because there is an entire sub-graph of objects in your application that are being deallocated. Since deallocation order is largely non-deterministic, you can't safely trigger any complex behavior in dealloc without risk that you are going to message an already deallocated object.
The best solution would be to get that cancellation mechanism out of dealloc!
Two things to check:
1. does your globalDataProvider (or any other class for that matter) have a reference to your controller (i.e. to call it back with data?) Your retain counts could be zero but for the wrong reason. How are you allocating the array, as well as each controller in the array? Do you have #properties?
2. In your executables properties Arguements screen, set NSZombieEnabled=YES That will tell you exactly which object is being called when it has a zero retain count.
HTH,
-Mike

memory leak when adding objects to nsarray

In the code below, PersonListArray is an NSMutableArray and I'm getting the list of persons from the sqlite DB and adding it to my array.
Person* tmpPerson = [[Person alloc] init];
tmpPerson.personName = #"Mike";
tmpPerson.personEmail = #"mike#mike.com";
[PersonListArray addObject:tmpPerson];
[tmpPerson release];
Even though I'm releasing the Person object here, its giving a memory leak which I'm guessing is due to the array holding a reference count to it. I'm using the array elsewhere in the program and then releasing it for sure.
What's the best practice to create new objects for an array and not run into this issue?
In the dealloc method where i release the array
-(void) dealloc{
[PersonListArray release]; // this contains the numerous Person objects
[super dealloc];
}
should i manually release them like this instead ?
-(void) dealloc{
for (int i = 0; i<PersonListArray.count;i++)
{
Person * tmpPerson = [PersonListArray objectAtIndex:i];
[tmpPerson release];
}
[PersonListArray release];
[super dealloc];
}
The code you are showing us is correct and contains no leaks. The last section is wrong, though, and would case your program to crash because you are releasing Person objects you no longer own.
Your code, as initially implemented, is correct. An array retains onjects added to it and releases them either when they're removed from the array or when the array is dealloced. No need to go through the array yourself.
What means are you using to detect the leak? If it's Instruments then you may be misunderstanding what it is telling you. When it detects a leak, it can show you where the memory was first allocated. It can't show you which object is responsible for the leak. I would therefore guess the given dealloc method is never called (because that object is leaked) or that someone else retains the array and doesn't release it. Try putting an NSLog in dealloc to ensure that it is occurring; as a run once test you could try logging PersonListArray after releasing it — if that doesn't cause a memory exception then almost certainly someone else has retained it.
[REMOVED: my original text "Try adding an NSLog of [PersonListArray retainCount] to your dealloc to figure out which is the case."; see comment from bbum below]
The most common cause of accidental additional retains is #property/#sythesize properties that are set to retain but for which a matching release is not added to dealloc.
Somewhere else in your app, you probably call [PersonListArray objectAtIndex:n] and pass it around to various other parts of your app. One of the other parts of your app is probably leaking it.
If you're using leaks, click on the particular "type of leak", then click on the memory address, and it'll show you the alloc/free/retain/release/autorelease history of that memory address. If you enable the detail view (Cmd-E I think), you'll see stack traces for all of those as well. Look for something that's doing a retain but not a corresponding release. (It's a bit difficult when things are retained by multiple autoreleased arrays...)

Memory management question

I wanted to clarify something:
I.e when I add a UIVIEW To i.e another UIVIEW programatically.
i.e [theview addsubview:thechildview];
I need to release thechildview because theview increments the retain count.
Right?
So basically if I have a loop and I am using that loop to initialise subviews and add the subviews to a view:
for(int i=0;i<10;i++){
UIView *child = [UIview alloc]init.....
[parent addSubview:child];
[child release];
}
I still need to release the child right ? So that would mean the child view still has a retain count of 1 right ?
The thing is whenever I call the release on the child after adding , the child gets deallocated after a while.
Can you guys confirm am doing the right thing by releasing the child after it is added ?
thanks.
You should release it somewhere, correct. You can hold on it and release it later, but it is your responsibility to do it somewhen.
The parent view will retain/release its childs on its own. So if the parent view gets dealloc'ed, it will release its child views.
If a child view is removed from its superview, it will also be released.
I need to release thechildview because theview increments the retain count.
Right
Wrong.
You need to release theChildView if you own it and if you have finished using it. Whether theview chooses to retain it or not is completely irrelevant.
So if we look at your loop
for(int i=0;i<10;i++){
UIView *child = [UIview alloc]init.....
[parent addSubview:child];
[child release];
}
the code is correct because at the beginning of the loop block you alloc the child which means you own it and at the end of the loop it is about to go out of scope which means you no longer need it so you must release (or autorelease) it.
What happens in between is totally irrelevant and you should not assume anything about what any particular method on another object does (except that it adheres to the Memory Management Rules).
I still need to release the child right ? So that would mean the child view still has a retain count of 1 right ?
You cannot assume anything about the retain count. parent might cause the object to be retained more than once if it hands it off to other objects. It might choose to copy it instead of retaining it. If it doesn't need to keep a reference it might not retain it at all.
Can you guys confirm am doing the right thing by releasing the child after it is added ?
No, you are doing the right thing by releasing it when you have finished using it.

How many times do I release an allocated or retained object?

I am making an iPhone game. I want to release all objects that have been allocated or retained. In the dealloc function I am releasing all such objects, but then I realized that sometimes I end up releasing objects when they have not been allocated yet. So I figured I need to check if its retainCount is greater than zero or not before I release it.
My question is:
Do I just check if the retainCount is greater than zero and then release it?
if([bg retainCount]!=0)
{
[bg release];
}
or
Should I release it as many times as its retainCount
while([bg retainCount]!=0)
{
[bg release];
}
Thanks for your help!
Do not use -retainCount.
The absolute retain count of an object is meaningless.
You should call release exactly same number of times that you caused the object to be retained. No less (unless you like leaks) and, certainly, no more (unless you like crashes).
See the Memory Management Guidelines for full details.
Autorelease makes retainCount meaningless. Keep track of retains & whether you own an object. Study & remember these rules: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-BAJHFBGH

iPhone cocos2d sprites in array, memory problems

I'm trying to keep track of my sprites in an array, add and remove
them from layers, and then finally clear them out of the array.
I'm using the following code:
Sprite * Trees[50];
Layer * Forest;
Forest = [Layer node];
Forest.isTouchEnabled = YES;
[self addChild:Forest z:30];
// do this a bunch of times
Trees[0] = [[Sprite spriteWithFile:#"mytree.png"] retain];
[Trees[0] setPosition:cpv(240,160)];
[Forest addChild:Trees[0] z:5];
And then when I want to destroy a tree I use:
[Forest removeChild:Trees[0] cleanup:YES];
[Trees[0] release];
My problem is that when I look in Instruments, I'm never reclaiming
that memory, there is never a drop back down. I thought that by
releasing the sprite it would free up the memory. Am I doing this
completely wrong?
I know that when you are using the simulator with cocos2d, the memory doesn't look like it's being released, so you have to run it on the device to get an accurate picture of what's going on.
There is a good discussion here about cocos2d and memory.
What I've noticed is that everything that you create and retain must be released, but it isn't released from memory until I do this:
[[TextureMgr sharedTextureMgr] removeAllTextures];
That will release the memory.
Here's a bigger example:
Sprite * sPopup = [[Sprite spriteWithFile:#"popup.png"] retain];
sPopup.position = cpv(240,440);
[self addChild: sPopup z:2];
[sPopup release];
Then, when I'm done with sPopup in another function I have this:
[[TextureMgr sharedTextureMgr] removeAllTextures];
and the memory is freed.
My suspicion is that you are "over" retaining:
Trees[0] = [[Sprite spriteWithFile:#"mytree.png"] retain];
If Trees is a local variable in a function you do not have to retain in that case if spriteWithFile is returning a Sprite with an autorelease.
The section on delay release in the apple documentation discusses this further. The long and short of it is that the receiver of the autorelease is guaranteed to have the object be valid for the duration of its scope. If you need the object beyond the scope of the function (e.g. Trees is a property of a class) then yes, in that case you need a retain (or just synthesize a property configured to retain).
By issuing the extra retain, it is likely that your retain count is always too high (never reaches 0) and hence your object is not garbage collected.
For good measure, I'd suggest reviewing this paragraph as well that talks about the validity of objects.
Even though you call [Trees[x] release], I believe you still need to 'delete' the item from the array, like Trees[x] = nil or something, as the array itself is still containing the object.
The 'retain' in the Sprite creation is also not necessary, as [Forest addChild:z:] will place a retain on it as well (afaik).