I am pushing my UIViewController subclass onto the navigation stack; however, since it is being retained by the navigationController, I 'release' my pointer to it after pushing it onto the stack so that when it is eventually popped the viewController will be dealloc-ed.
However, it's not working, the dealloc method for the viewController is never called. The code looks something like this:
MyViewController *newViewController =
[self.storyboard instantiateViewControllerWithIdentifier:#"foo"];
[self.navigationController pushViewController:newViewController animated:YES];
newViewController = nil;
Even in the following code, my newViewController is not being dealloc-ed:
MyViewController *newViewController =
[self.storyboard instantiateViewControllerWithIdentifier:#"foo"];
newViewController = nil;
From what I understand, under the new Automatic Reference Counting (ARC) system, objects are dealloc-ed once nothing points to it. I put a NSLog method in the dealloc method of the viewController that is being created, but it is never called.
What am I missing here?!
Thanks for reading my first post on stack overflow :)
**EDIT:**
I apologise. I tried the second piece of code wrapped in an autorelease pool and it was deallocated. I then tried it without the autorelease pool and it deallocated properly too. I don't know what happened last night.
(2nd edit: and now its stopped working again. fffffffuuuuuuuuuuuu)
In your first example, it would be very odd if the new view controller were deallocated, since you just pushed it onto the navigation controller, and thus the navigation controller has a strong reference to it. The object will only be deallocated when ALL strong references are gone.
In your second example, it's possible that the returned object still has pending releases from autorelease calls that were performed internal to the implementation of instantiateInitialViewController. Thus, until the current autorelease pool is drained, you wouldn't see it disappear. Further, it's possible that the UIStoryboard class caches the returned object as an optimization.
In short, even with ARC you still cannot assume that objects you receive from framework methods will be deallocated immediately when you're done with them, because you cannot guarantee that autorelease was not used in that method's implementation.
Related
Do I need to call release here or not?
I am Loading view in didSelectRowAtIndexPath in tableview...
EventDetailedViewController *eventDetailedViewController=[[EventDetailedViewController alloc]initWithNibName:#"EventDetailedViewController" bundle:nil];
eventDetailedViewController.aEventInfo=aEventInfo;
[self.navigationController pushViewController:eventDetailedViewController animated:YES];
// [eventDetailedViewController release];
eventDetailedViewController=nil;
When I need to call [eventDetailedViewController release]; and when I need not to call [eventDetailedViewController release]. didSelectRowAtIndexPath in tableview...
EDIT:
I do have three views when I select it, it loads first nib. From first it loads second nib file. From second it loads third nib file. When I get back from third to second to first, my application crashes... I am thinking it's due to releasing my first view controller.
Apple's memory management rules are very simple: every call to alloc/new/copy*/retain must be balanced by a call to auto-/release. Anything (object or code block) that calls the former has ownership; when the owner no longer needs the owned object, release it. self.navigationController, for example, will retain a pushed view controller (asserting ownership) until the controller is popped form the navigation controller's stack. The only thing this simple(-istic?) explanation leaves out is when to use weak references (necessary to prevent retain cycles).
Since eventDetailedViewController is a local variable, it's the code block that owns eventDetailedViewController. Since the variable isn't static, the variable will be discarded when the block exits and the block can't make use of the object after that, so the object needs to be released.
Since you're are not going to use eventDetailedViewController (you're setting it to nil), you should release it. The UINavigationController's pushViewController method will retain the object so it won't be released until the navigationController no longer needs it.
Setting it to nil, I believe, is only useful if it is a property that's nonatomic where setting it to nil will release the previous value. Since it's a local variable, you should release it.
I kind of understand why I'm getting this analyzer warning. Because I'm using an object that is being passed in. I've tried autorelease and retain however these cause me other problems like unrecognized selector sent to instance.
The aim of my CommonUI function is to re-use code, but I have to cater for addSubView and presentModalViewController.
Perhaps I'm doing some obvious wrong ?
Change your code like this:
HelpViewController *helpvc = [[HelpViewController alloc] init....];
[vw addSubview:helpvc.view];
[helpcv release];
I think you don't need to pass the other VC.
There are two problems here.
First, if you call [vc release] (as the other answers suggest), you'll certainly make the analyzer happy but likely crash the app. A view controller's view doesn't retain the controller, so any button targets in the view will be pointing to garbage.
You will need to somehow keep the HelpViewController retained for as long as it is showing up onscreen. The "parent" view controller should likely retain it somehow. You could autorelease it, and return it. Then whomever calls showHelpClick... would retain the returned controller.
Second, you don't need to have the (UIViewController *)vc passed in as an argument.
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.
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.
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.
I have a UIViewController (parentVC) to which I add a UITableViewController as follows (it is not pushed since the tableview only occupies about half the screen):
tableVC = [[SmallTableVC alloc] initWithStyle:UITableViewStylePlain];
[self.view addSubview:tableVC.tableView];
In dealloc, I add [tableVC release];
Using instruments, I can see that the tableVC is indeed fully released when parentVC released, which is good, but I am not sure why as I thought that the UITableView of tableVC would have a retain count of 2 (1 for retention by tableVC and 1 for retention by parentVC). My intuition was that an additional [tableVC.tableView release] would be required, but adding it crashes the app. Why is tableVC released properly by the current code (if indeed it actually is)? Does a UITableViewController not retain its tableView? Thanks.
You are allocating tableVC, so retain count is 1. You do not pass tableVC to anything else, so there is no reason anything else would retain it. If something else did retain tableVC, it would be responsible for releasing it when it no longer needed it.
If you were to look at the retain count of the UITableView owned by tableVC it might be higher than one, as it is retained by both tableVC and, after you call addSubview, the view owned by parentVC.
Edit:
If you want to forcibly clean up tableVC.tableView then call removeFromSuperview before releasing tableVC. I believe that view controllers implicitly do this when released, but it does not hurt to do it manually as well.
After you call [self.view addSubview:tableVC.tableView] tableVC.tableView will have a retain count of at least two. One for the view hierarchy and one for tableVC. When you release tableVC it is removing the tableView from the view hierarchy which releases tableView, then it is releasing tableView itself. Even if it was not removing tableView from the view hierarchy, parentVC.view is about to be torn down and release the view hierarchy that included tableVC.tableView.
Only release what you have explicitly retained. The naming conventions make this pretty clear. If the name includes retain, new, alloc or copy then you should release the result.
Be careful trusting retain counts on iPhone. iPhone does not do real garbage collection and therefore these values should be ignored. Follow the memory management rules and you will be fine.
In this case, the tableVC will be released in "dealloc" and the view it controlled will be released in [super dealloc].
Do not trust retain counts!
is my UIViewController freed when i call another controller ?
How to release memory of controller when i push to another controller ? i take more than 40Mo and application leave because of LOW MEMORY(No leak).
[self.navigationController pushViewController:[[MainListController alloc] init:self] animated:NO];
#interface MainListController : UIViewController
...
- (id)init: (id)ref;
when i call on init function the dealloc of controller i was :
[ref dealloc];
i've this error :
objc[70754]: FREED(id): message isEqual: sent to freed object=0xe216b0
Program received signal: “EXC_BAD_INSTRUCTION”.
(void)dealloc {[super dealloc];}
You cannot (and should not) release/dealloc a parent view controller when pushing a new one onto the navigation stack. You can unload the view by responding to the didReceiveMemoryWarning message.
There are several other problems with your code:
Never ever call [obj dealloc] manually. Always use [obj release].
Your init method name should be more descriptive as to not conflict with the predefined init method. You should also (possibly) strongly type the ref argument if you need to use it. Something like (id)initWithParent:(UIViewController*)ref would be better.
Your code is leaking a MainListController object, since you are alloc/init'ing it and then handing it to navigationController. Change your code to [[[MainListController alloc] initWithParent:self] autorelease].
No, you can't dealloc UIViewControllers that are not visible. What happens when the controllers on top pop off the navigation controller? What would the OS display then?
Instead you should implement the didReceiveMemoryWarning method in all your controllers. These should release (not dealloc) as much cached data as possible. Anything that you can recalculate or bring back in from disk should be considered.
This is better than just deallocing views that are not visible as you really don't know how much memory is available in advance. On an iPhone 3GS you might have plenty of memory left even when using 40Mb.