views:

109

answers:

2

Hi,

I recently changed my app to use a UINavigationController, I was using a UINavigationBar before, with cascade subView adding, which was a bit tenuous.

I'm facing a problem of memory usage. Leaks tool doesn't show any leak, but ViewControllers I create and add to the UINavigationController never seem to be released. So memory usage grows everytime I create a new VC and then press the NavigationController's back button.

I simply create and add my VCs this way:

DetailViewController* detailViewController = [[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil];
// setups
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];

The app never goes through ViewController's dealloc and viewDidUnload methods. Shouldn't these be called everytime I press the back button?

I've searched many tutorials and read Apple's memory management, but there's nothing about VC's lifetime in memory when using NavigationController.

+2  A: 

I have seen this as well. As you pointed out, I haven't seen anything definitive in the docs, but my belief is that they are retained in memory until memory is needed. It makes sense from a performance perspective as doing so allows the app to quickly navigate between the different views.

Bottom line is, I wouldn't worry about it. You could fire off some Low Memory Warnings in the Simulator and see if it actually releases your VCs.

Jason McCreary
Thanks for your answer. I just tried sending a Low Memory warning in the simulator after creating and leaving about 10 VCs. All VCs viewDidUnload methods were called, but no dealloc at all! And, according to their addresses in memory, none seem to be reused, so memory usage keeps growing bigger.
Jukurrpa
I suggest taking a look at the Apple Developer Forum. You may find a much more detailed answer from one of the Apple DTS Moderators.
Jason McCreary
I've never been on this forum, I'll might take a look, thanks.
Jukurrpa
+1  A: 

Maybe you are not doing something wrong and instead you are facing something like this

In the Blog post it was the question whether we have to manually release IBOutlets or not. As it turns out we should. This was reproduceable in iOS 3.1.3 but I didn't test it in iOS 4.0 yet.

The second aproach is to override your view controllers retain and release method and print out the retain count. I had a simimlar problem, that some view controllers dealloc method did not called so I override this methods to see wether someone has still a retain on it. As it turns out it did.

Edit:
When I printed my retain count, it would sometimes reach ~98 caused from the framework, so thats not really to worry.

If your last retain count stays at 2 and the dealloc method won't be called, than there is someone that has still a retain on it.

In this case you should search on other places.

For example another problem I encountered during this same problem: Sometimes I would use

[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateUI) userInfo:nil repeats:YES]

to constantly update the UI. But what I forgot was, that the NSTimer will retain the target object (which was the ViewController). Because the NSTimer retained your view controller your dealloc will never be called because someone (NSTimer) has still a retain on it. So you have to make sure to invalidate the NSTimer BEFORE dealloc method to properly release the view controller.

Edit2 in response for a comment below:
A retain declaired property does as follows (exsample):

- (void)setTarget:(id)value {
  if (value != target) { 
    [target release];
    target = [value retain];
}

So it does first release your current self.target then retains the new value. Since you are assigning nil your target will be nil afterwards. Further info about Properties can be found in the Apple doc.

Buju
Well I do release every outlet in the dealloc method. But it has no effect as it is never called. I've tried to override release and retain, besides the fact that the retainCount reaches 50+ at some point (caused by the subviews?) it remains to 2 after the last release (when I've left the view). I don't see why it is so, as I only use it in the code I've posted.
Jukurrpa
edited my answer
Buju
That helped me fixing one of the remaining retains, thanks. I'm not using a NSTimer but I'm using a wrapper for ASIHTTPRequest which had DetailViewController as a delegate for some actions. I just had to release it when DetailVC viewDidDisappear is called.Maybe the last retain is due to something like this too, I'm going to keep searching.
Jukurrpa
I just did fix the second remaining retain by adding "self.target = nil" after releasing it in the wrapper I talked about before. (target being DetailVC, stored as an id with retain property). What could possibly explain this?
Jukurrpa
Hm, in fact that does not seem to be the solution. This randomly causes a crash when I leave the view... Damn memory management. Wouldn't auto/shared pointers be simpler? :(
Jukurrpa
HTTPRequest sound like you are using some kind of async operations? If thats the case, you should also be aware that when you are calling a target selector when your request did finish, that your target might just be released (maybe through closing the view) and your app will crash because you are calling something on a released object.
Buju
Yep that's for async http requests. But all requests are cancelled when I release it, so it doesn't call my VC's selector. Do you have any idea about the self.target = nil which lowers the retain count although I did a [self.target release] just before?
Jukurrpa
edited my answer again because it contains code formatting.
Buju
Oh right, I even came accross this while reading Apple's memory management documentation. Thanks.
Jukurrpa