I have come across previous problems which occur when I release a viewController after pushing it via the UINavigationController, so I now autorelease every viewController that will be pushed. But I often see code where the developer releases the viewController after pushing it.
My question is, when is the correct time to release/autorelease a UIViewController when pushed onto the stack?
Thanks
push
release
The navigation controller retains the view controller when you push it.
I would suggest any of your previous problems were nothing to do with the above process, but elsewhere.
ViewController *yourViewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[self.navigationController pushViewController:yourViewController animated:YES];
[yourViewController release];
Whether you autorelease the view controller before the push or release the view controller after the push makes little difference. The release after the push is a bit more efficient since it would cause the view controller to be released sooner (freeing up memory faster) but the autorelease will also release it soon, but a bit later.
Related
I have two view controllers VC1 and VC2. i try to push VC2 by creating object of VC2 and pushing it on button action.
VC2 *vcOject = [[VC2 alloc]init];
[self.navigationController pushViewController:vcOject animated:YES];
[vcObject release];
in VC2 i am popping it in button action..
[self.navigationController popViewControllerAnimated:YES];
now i am in VC1 and again Pushing VC2 for display. but this time i am getting EXC_BAD_ACCESS error. but when i commented [vcObject release]. it works fine. but when can i release memory. how memory is handling in navigation controller.
Simple answer: You should be using ARC.
If for whatever reason you can't, your view controller is deallocated once it's popped off the stack, and you should alloc/init a new one every time you want to push it on to the stack. Or if you don't want to do that - say, if the view in VC2 is very heavy - get rid of your [vcObject release] and keep a reference to vcObject in VC1, and keep pushing the same one on to the stack. Make sure you don't mix these two, though; if you create a new object every time you must release it after pushing it on to the stack, and if you are keeping a reference to push the same VC2 on the stack each time, you must not release it until VC1's dealloc. Keep in mind that you must release it in VC1's dealloc method, or else the memory will be leaked.
But, really. You should be using ARC.
If you don't use [vcObject release]. it will cause memory leaking to your application.
so, This is the way to handle memory management with push/pop viewController without using ARC
VC2 *vcOject = [[VC2 alloc] init];
[[self.navigationController pushViewController:vcOject animated:YES] autorelease];
First: I ported my App to ARC and everything seemed to work. But now I discovered a problem: I have a UINavigationController that is presented modally with some UIViewControllers on its stack. But when I dismiss the modal view controller, the view controllers from the stack don't seem to be deallocated. Here is what I do:
UIViewController* root = [[UIViewController alloc] init];
UINavigationController* navi = [[UINavigationController alloc] initWithRootViewController:root];
[self presentModalViewController:navi animated:TRUE];
Then from the root I push some more view controllers, but that doesn't really matter. The fact is when I later call
[self dismissModalViewControllerAnimated:TRUE];
root doesn't get deallocated. Of course in my code root is a subclass of UIViewController, and I track dealloc and viewDidUnload, but nothing gets called.
Any ideas?
What's inside your navigation controller? It could be that something else (perhaps a view controller inside your navigation controller) is the culprit, which is leading up the chain meaning the navigation controller doesn't get released.
Either way, the code you posted is correct, so if your navigation controller isn't being released after calling dismissModalViewController it would suggest that something else still has an active reference to it or one of its dependencies. I know that doesn't answer your question, but you will probably have to hunt around.
Since you aren't showing actual code, it's hard to tell what is going on with your root view controller.
But, with ARC, if you have a strong pointer to an object, it won't get released. I suspect that you are holding on to this controller after adding it to your navigation controller.
But, without seeing your code, I can't tell.
From what I understand pushViewController should release old viewController when a new one is pushed?
Here I just creates two different viewControllers and pushes them.
UINavigationController *navController = [[UINavigationController alloc] init];
[self.window addSubview:navController.view];
smallLayout = [[SmallViewController alloc] init];
[navController pushViewController:smallLayout animated: NO];
[smallLayout release];
largeLayout = [[LargeViewController alloc] init];
[navController pushViewController:largeLayout animated: NO];
[largeLayout release];
In the SmallViewController dealloc is never getting called and when I'm checking retain count it's still 1. I'm checking retain count long after the run loop is done and I also know that retain count isn't something you should trust.
No it should not....
The navigation controller maintains a navigation stack of all the view controllers pushed on to it... so when you go back or pop the current view controller, the previous controller is still present.
The navigation controller will release a view controller after it is popped.
The view controllers don't get released when you push a new controller onto the navigation stack. The navigation controller stays holding onto them so that it has the correct item to display when you pop the current controller off of it. If it was releasing it, then the nav controller wouldn't have anything to go back to.
If you're looking to try to optimize memory, implement -(void)viewDidUnload. It gets called when ever the controller's view gets unloaded which may happen when you push the new controller. I say may happen since it is called during low-memory conditions. So if you have plenty of free memory it won't get called. In the simulator you can force it by simulating a memory warning. Make sure that anything you destroy in it can be, and is, recreated in -viewDidLoad.
You alloc once, you release once. You are already doing it in your code. So AFAIK your code is fine. Here dealloc of smallLayout won't get called because UINavigationController keeps a stack of all viewControllers pushed into it, hence retaining it. UINavigationController manages the release of these viewControllers, when it is no longer needed.
Each time I push a new viewcontroller, it adds about 3MB. TestVC is a brand new VC with one method for pushing a new version of the VC.
UIViewController *vc = [[TestVC alloc] initWithNibName:nibName bundle:nil];
[self.navigationController pushViewController:vc animated:YES];
[vc release];
When I popviewController, it doesn't release any memory (watching activity monitor).
[self.navigationController popViewControllerAnimated: YES];
So, as I add navigate through the app (about 60 different pages), memory keeps building up. Does initWithNibName need something special. When I pop, do I need to release the nib somehow?
The most likely problem is a failure to release something in -[TestVC dealloc]. I would evaluate that method by inspection first. If you can't find the problem, use the Leaks instrument in Instruments to find what particular thing is being over-retained. If Leaks doesn't find it, then use the heapshot tool in Instruments to see what's being allocated. With something so large, it should be easy to find. There's a quick overview of using Heapshot on Use Your Loaf.
I was watching CS193P Stanford course on Itunes, and in one of the lectures a demo was given and
There it was said you could present the viewcontroller modally and then release it. Roughly like this (I know this isn't perfect but I'm on my PC atm)
[self.view presentcontentmodally:myVC]
[myVC release];
However this seems to produce problems. If I put a NSLog(#"%d", [myVC retainCount]) between those two lines then it returns 2 implying it is ok to release. However when I dismiss the myVC the app crashes. Nothing in the NSlog and the debugger won't show where it stopped.
But I used malloc-history or something that some blog said would help. And found that it was the myVC.
So should I be releasing myVC?
(also when the modalVC has been dissmissed should the app's memory usuage go back to before the modalVC was presented?)
Yes, you should release your view controller after passing it to a modal navigation controller. Just make sure you are not passing in a previously retained view controller unless you plan to manage its release manually.
For example, follow the lifespan of _myViewController here:
MyViewController *_myViewController = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
UINavigationController *_modalNavigationController = [[UINavigationController alloc] initWithRootViewController:_myViewController];
[_myViewController release], _myViewController = nil;
[[self navigationController] presentModalViewController:_modalNavigationController animated:YES];
[_modalNavigationController release], _modalNavigationController = nil;
The modal navigation controller will increment the retain count of _myViewController, essentially taking ownership of it.
Once the modal navigation controller is dismissed and you are back to your original navigation controller, the modal navigation controller will receive a release message and in turn release its root view controller (_myViewController).
The retain count of this view controller will hit zero and the memory is then reclaimed.
I have just checked through a couple of my apps, and I am releasing my modal view controllers after each presentation, without problems. Which makes me think that you don't yet understand the Cocoa memory management model. Here's a sample:
TweetController *tweetController = [[TweetController alloc] init];
tweetController.content = content;
tweetController.delegate = self;
[self presentModalViewController:tweetController animated:YES];
[tweetController release];
Note that this controller was created with alloc/init, and wasn't previously released or autoreleased.
In addition, please don't rely on retain count checking; a retain could be from a previous autoreleased, which will go away very soon causing the sort of error you have been seeing.