This may sound a newbie question ... but however I'm new to iOS development.
I've created UITabController object programmatically like this.
mTabBarController = [[UITabBarController alloc] init];
...
mTabBarController.viewControllers = [NSArray arrayWithArray:tabBarItems];
[tabBarItems release];
And releasing mTabBarController in dealloc like this.
- (void)dealloc {
[mTabBarController release];
}
Now my question : will I get a memory leak ? When I assign value t viewController the ref count of tabBarItems is still 1. When I release mTabBarController does it also release all its viewcontrollers ?
Yes, the tab controller owns an array of view controllers (and everything in an array is retained). You're not creating a leak as long as you properly release or autorelease the items you're adding to the tabBarItems array.
It really helps to think of object relationships as ownerships.
UITabBarController should never be placed as the child of another ViewController, so you will always have to release it in dealloc. If your TabBarController's view is the childview of your application's window, it's ok not to release it in dealloc since the only time dealloc will ever get called is when your program is closing, in which case your controller will be released anyways. However, some people like to release it in dealloc anyways just to keep their code consistent. What you're doing is fine.
Related
I'm presenting a modal view with the UIModalPresentationFormSheet presentation style.
MPMediaItemCollection *albumItem = [self.albums objectAtIndex:index];
AlbumViewController *destination = [[AlbumViewController alloc] initWithAlbum:albumItem];
destination.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:destination animated:NO];
When the user taps outside the modal view, it is closed. However, it's still in memory, it's not released.
What's the correct way to deal with this?
Edit: is seems there's something inside AlbumViewController that's not being released properly. A bug on my end.
Yes, this is correct. ARC will take care of deallocating the memory when it is needed.
From here:
ARC deallocs any object to which there are no more strong references. So to dealloc something, simply set all the variables pointing to it to nil and make sure the object is not involved in any circular reference.
Here is how I remove UIViewControllers which never will be used again in the app.
NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray: self.navigationController.viewControllers];
[allViewControllers removeObjectAtIndex:0];
[allViewControllers removeObjectAtIndex:1];
Is this the right way to remove view from the stack ?
While running the app with Instruments I notice that the memory is not freed when the app enter the stage where the code above is executed. What is wrong here ?
Creating a new NSMutableArray of the viewControllers and then removing from the new NSMutableArray accomplishes nothing. The viewControllers still are retain by the navigationController.
By assigning your view controllers into a new mutable array you increased their retain count by 1, removing them decreases their retain count by 1 so the net effect is zero. What you want to do is replace the view controllers that are part of the navigation controller after removing them, there by making the navigation controller release your old instances.
NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[allViewControllers removeObjectAtIndex:0];
[allViewControllers removeObjectAtIndex:1];
[self.navigationController setViewControllers:allViewControllers animated:YES];
you are not actually freeing up the viewControllers. you are just increasing their retain count by +1 and then decreasing by -1.
actually nothing is getting affected by the code you have written to free your viewControllers.
For a NavigationController, you call popToRootViewController to release all other view controllers except the base/root viewcontroller for NavigationController.
I have been under the assumption for a while that viewDidUnload is always called when a controller is deallocated. Is this a correct assumption?
I've just been exploring some odd things, and set a breakpoint in my controller's viewDidUnload and it's dealloc. It appears that dealloc is called, but the viewDidUnload method is never called. I even added a self.view = nil to my dealloc and it still didn't seem to call it.
Does this mean that retained view objects I have been releasing in the viewDidUnload method also need to be released in my dealloc method to be sure they really go away?
I know there are many other questions on StackOverflow about viewDidUnload, but none specifically address this issue about duplication of release statements between the 2 methods.
A more concrete exmaple in a fresh project on the 3.1.2 SDK:
#implementation TestViewController
#synthesize label;
- (IBAction)push {
TestViewController *controller = [[[TestViewController alloc] initWithNibName:#"TestViewController" bundle:nil] autorelease];
[self.navigationController pushViewController:controller animated:YES];
}
- (void)viewDidUnload {
self.label = nil;
NSLog(#"viewDidUnload was called");
}
- (void)dealloc {
[super dealloc];
NSLog(#"label retain count: %i", [label retainCount]);
}
#end
My app delegate creates a simple navigation controller with one of these as it's root controller. When I tap the button linked to push 3 times, and then hit the back button three times, the following output is generated.
ViewDidUnloadTest[2887:207] label retain count: 2
ViewDidUnloadTest[2887:207] label retain count: 2
ViewDidUnloadTest[2887:207] label retain count: 2
Which is 2 higher that I would think it would be. Retained once by the view and once by the controller. But after the dealloc I would have expected the view to be gone releasing my label, and the controller to be gone calling viewDidUnload and releasing it. Although there may be an autorelease in there throwing off the count at this point.
But at least it's clear that viewDidUnload is not getting called at all, which contrary to this answer here: Are viewDidUnload and dealloc always called when tearing down a UIViewController?
Perhaps I should simply call [self viewDidUnload] in all my dealloc methods on controllers? Worse than can happen is that I set a property to nil twice, right?
Unless you need to break a retain cycle, you should generally only be releasing objects in your dealloc method. viewDidUnload is an exception; it is invoked in low memory situations and should be used to release anything that you can.
If you do need to release them anywhere else, then always set the reference to nil after the release. That'll protect your app from blowing up later (likely in dealloc).
Note that the documentation quite explicitly calls out that the view property will already be nil when viewDidUnload is called.
I'm developing an iPhone app, I'm trying to push a view into the navigation controller, which I've done many times before, however, I'm having some issues with this particular app. I have a table view, and when the user selects one row the new view is pushed into the controller:
DataWrapper *row=[[self.rows objectAtIndex:[indexPath section]] objectAtIndex:[indexPath row]];
DataViewController *nextController=[[DataViewController alloc] initWithNibName:#"Data" bundle:[NSBundle mainBundle]];
[nextController setInfo:row];
[nextController setRow:[indexPath row]];
[nextController setParent:self];
[self.navigationController pushViewController:nextController animated:YES];
[nextController release];
and it goes fine, until the user taps the back button, I get an exception, and used NSZombieEnabled and get this:
-[DataViewController respondsToSelector:]: message sent to deallocated instance 0x4637a00
So i tried to remove the [nextController release] and in fact it worked, but WHY???? I allocated nextController, so I'm supposed to release it, right?? I don't feel right releasing this app if there's something like this, I feel like it's going to fail. Please let me know your thoughts.
Your nextController isn't being retained by navigation controller. If you release it then because there is only one init/release pair, the object is deallocated. Later when the navigationController attempts to send messages to it, you get the error you see.
This is also why remove [nextController release] fixes the problem.
You are right in that if you allocated, you should free it. But the caveat is only after your application is done with it, not before.
Some objects will stay allocated for nearly the lifetime of the application, so don't feel too bad.
I would guess that [self.navigationController] is returning nil, because if it weren't nil, it would be retaining your object. Since your object is not getting retained, it would appear that there is no object that's trying to retain it, indicating that the navigationController property is empty.
Is it possible that your navigation controller is somehow being deallocated, resulting in the view controllers also getting released? You could maybe test it by retaining the nav controller just before pushing nextController.
For debugging purposes, I would override -dealloc in your DataViewController class, and set a breakpoint on it.
but it seems to work for something like:
DetailViewController *controller = [[DetailViewController alloc] initWithNibName:#"SomeView" bundle:nil];
controller.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController: controller animated:NO];
[controller release];
so, why is it not working for pushViewController ?
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
AViewController * aView = [[AViewController alloc] initWithNibName:#"myNib" bundle:[NSBundle mainBundle]];
return vintagePriceRowView.view;
}
I know this code needs a release... somewhere. But where? If I set the allocated AviewController to autorelease, then touching elements within the view results in a "message sent to deallocated instance 0xfb5780"
The Leaks instrument is not actually showing a leak, but obviously Clang does not like the above code. I know Clang is not the gospel as far as determining everything that could possibly be wrong in your code, but in this case, it feels like it is probably right. I've allocated it, I need to release it.
Any ideas on what I am doing wrong?
For efficiency's sake, you should create the view that you'll be using in the footer before it's required. Maybe create it in viewDidLoad in your tableViewController and store it in a member variable.
Then in your viewForFooter method simply return the view you stored earlier.
Then in your tableViewController's dealloc method, release the view.
You need to hold on to it until it is not longer needed.
I suggest making a private property, set it to nil initially, and then lazy load the nib and assign the returned view to the property. Then in dealloc or in viewDidUnload simply set it to nil via the setter.
You will of course need to release or autorelease once you assigned it to the private property, since the setter will retain it for you.