Within my mainViewController I'm adding a view from another ViewController. I'm removing it's view when finished. What is the proper way to manage this and where do I release the view controller?
mainVc.m
-(void)showView {
helpPage *elementController = [[helpPage alloc] init];
[self.view addSubview:elementController.view];
}
helpPage.m
-(void)removeView {
[self.view removeFromSuperview];
}
Since you called alloc init on the controller in that instance of mainVc, that instance of mainVc owns releasing the controller.
When it calls addSubView, it will retain the view (and add to view hierarchy) and when it's removed from the superview, it will be released. At that point, when it's release, the controller that created the view is still retaining it as well. When both have released the view, it will go away.
Here's a related SO post:
Does UIView's addSubview really retain the view?
I think you are looking for something like this
mainVc.m
-(void)showView {
helpPage *elementController = [[helpPage alloc] init];
[self.view addSubview:elementController.view];
[elementController release];
}
helpPage.m
-(void)removeView {
[self.view removeFromSuperview];
}
Granted, I haven't seen the rest of your code, so that might not be what you are looking for. However, in terms of memory allocation, any time you alloc something, you must release it later. In the case of views and view controllers, once you add that view or view controller, you can release the copy you "alloc'ed."
Related
I have some pretty simple code where I am using a UINavigationController and adding a rootViewController. After some processing has occurred I want to pop off the current view controller and replace it with another one. This seems to work fine, but my original view controller does not dealloc. I set a breakpoint in it's dealloc and it never gets hit. Below is my code. Not sure why happens. Just for testing if I release startController twice it does go away.
StartViewController *startController = [[StartViewController alloc] initWithNibName:#"StartViewController" bundle:[NSBundle mainBundle]];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:startController];
[nav pushViewController:startController animated:NO];
self.navController = nav;
[startController release];
[nav release];
Thanks any help.
Your ultimate goal is to bring the view controller's retain count to zero. So make sure that everything the view retains is released and anywhere the view is retained also release.
Please check the following possible causes:
Make sure you pop the view controller from the navController if you have a custom back button. The default Back button will work fine.
Make sure that all your IBOutlets are set to nil in viewDidUnload
- (void)viewDidUnload
{
[super viewDidUnload];
self.webView = nil;
}
If your view is an observer to a model class to receive events
For example
model.addObserver(myView);
and sure to also do
model.removeObserver(myView);
I hope this helps.
It looks as though your self.navController has got a holding reference to it. maybe put
self.navController =nil;
somewhere appropriate, so that when the view has been popped it is released.
I was trying to pop off the root view controller. I instead used the setViewControllers message from the UINavigationController object to replace all my view controllers.
recently I joined two Xcode projects together. To get this thing to work, I had to alloc and initialize my view controller.
self.myViewController = [[MyViewController alloc] init];
But why? In the other project I have the same code. The sole difference is hierarchy of the different views. I added a new view to the top (beginning). So the calling View Controller is not the first view on the stack anymore.
I'm pushing my view in this way on the stack:
[[self navigationController] pushViewController:myViewController animated:YES];
In my NIBs I have added a View Controller object with IB and connected the Outlets.
And I have a memory management question too: If I have a property like myViewController, do I have to release it? The "normal" release is done in the dealloc method. But do I have to use an additional release because of the alloc? I don't think so, but I ask you anyway.
I would need to see more code to answer why you had to alloc your view controller, but I'd say that you always alloc them manually (at least in my experience).
As for the memory management question, if your property is declared as a retain property (#property(retain) UIViewController *myViewController), you are indeed leaking memory, since the retain count after alloc will be 1, and after the retain done by your accessor will be 2. Hence, if you release it only once, you'll end up with a leak.
I usually do this instead:
self.myViewController = [[[MyViewController alloc] init] autorelease];
I found it out: In IB I had to set the nib name on my view controller object. So the alloc and initialization is done by IB?
There is one more option:
(IBAction)loginButton:(UIButton *)sender {
NSLog(#"pressed login");
ICMasterViewController *controller = [[self storyboard] instantiateViewControllerWithIdentifier:#"mainnav"];
[self presentViewController:controller animated:YES completion:nil];
}
On your storyboard you must have UIViewController with name mainnav
I'm somewhat unclear on the object ownership patterns required for the following instances. When my UIViewController presents a popover controller, an action sheet, or another view controller as modal, am I required to hang onto a retained reference to that child controller until it's been dismissed?
In other words, do the following lines of code effectively "transfer" ownership, or not?
[aPopoverController presentPopoverFromBarButtonItem:someButtonItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];
[anActionSheet showFromBarButtonItem:someButtonItem animated:NO];
[aViewController presentModalViewController:someOtherViewController animated:YES];
Can someone point me to explicit documentation on this subject?
UIPopoverViewController has a slight different memory management/owning. Present a popover does not retain the memory, so you can't transfer the ownership of your popviewcontroller to the presenting object.
To avoid memory leak, you have to adopt the UIPopoverControllerDelegate and implement the DidDismissPopOver method as follow:
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
[popoverController release];
}
This way, you can safe alloc and present a PopOver:
-(void)showSearch:(id)sender {
SearchViewController *searchVC = [[SearchViewController alloc] init];
UIPopoverController *popVC = [[UIPopoverController alloc] initWithContentViewController:searchVC];
popVC.delegate = self;
[popVC setPopoverContentSize:CGSizeMake(320, 100)];
[popVC presentPopoverFromRect:CGRectMake(200, 200, 320, 100) inView:self.view permittedArrowDirections:0 animated:YES];
[searchVC release];
}
Presenting a modal view controller retains the UIViewController. This is actually not clear from the docs. However, I tested it using the following code...
NSLog(#"BEFORE %d", [self.setupViewController retainCount]);
[self.navigationController presentModalViewController:self.setupViewController animated:YES];
NSLog(#"AFTER %d", [self.setupViewController retainCount]);
The self.setupViewController is already retained locally, but presenting it output the following:
2010-05-19 10:07:36.687 LocateMe[27716:207] BEFORE 1
2010-05-19 10:07:36.762 LocateMe[27716:207] AFTER 3
So it is probably being retained in the local modalViewController property, as well as in the view hierarchy. Dismissing it will balance these.
So bottom line is, retain it if you want to control it directly, but you don't have to.
EDIT - Just to be clear, the correct pattern is to always retain an object if you set yourself as its delegate. That's because you should be setting the delegate to nil in your dealloc for safety. Practically though, a modal controller is always going to be dismissed before you dealloc, so it's not an issue. You'll notice Apple also breaks this rule in [UIView setAnimationDelegate:], which actually retains the delegate you set.
- (void)launchSearch
{
EventsSearchViewController *searchController = [[EventsSearchViewController alloc] initWithNibName:#"EventsSearchView" bundle:nil];
[self.navigationController pushViewController:searchController animated:YES];
//[searchController release];
}
Notice the [searchController release] is commented out. I've understood that pushing searchController onto the navigation controller retains it, and I should release it from my code. I did just alloc/init it, after all, and if I don't free it, it'll leak.
With that line commented out, navigation works great. With it NOT commented out, I can navigate INTO this view okay, but coming back UP a level crashes with a *** -[CFArray release]: message sent to deallocated instance 0x443a9e0 error.
What's happening here? Is the NavigationController releasing it for me somehow when it goes out of view?
The boilerplate that comes on a UINavigationController template in XCode has the newly-pushed controller getting released. But when I do it, it fails.
---EDIT----
So this morning, I sit down, and it works. No real clue why. Sigh.
Taking what I thought I learned, then, and applying it to another piece of this same controller, I did the following. Yesterday I had this code WITHOUT the release statements, because it didn't work right with them. So this morning I added them to create:
- (IBAction)switchView:(id)sender
{
UISegmentedControl *seg = (UISegmentedControl *)sender;
NSInteger choice = [seg selectedSegmentIndex];
NSArray *array = [mainView subviews];
UIView *oldView = [array objectAtIndex:0];
[oldView removeFromSuperview];
if (choice == 0) {
tableController = [[EventsTableViewController alloc]
initWithNibName:#"EventsTableView" bundle:nil];
[mainView addSubview:tableController.view];
[tableController release];
}
if (choice == 1) {
calendarController = [[EventsCalendarViewController alloc]
initWithNibName:#"EventsCalendarView" bundle:nil];
[mainView addSubview:calendarController.view];
[calendarController release];
}
if (choice == 2) {
mapController = [[EventsMapViewController alloc]
initWithNibName:#"EventsMapView" bundle:nil];
[mainView addSubview:mapController.view];
[mapController release];
}
}
With it set up like this, when I come onto the view, the main portal of my view is filled with the EventsTableViewController's view, I can click to mapView and calendarView, but when I go BACK to tableView, I die because the table delegate methods are being called on a deallocated instance.
So I went and made all of these controllers into synthesized properties, so I can release them in [dealloc]. Which seems to work, but the real question is why adding these views as subviews doesn't retain them, passing ownership to the new view it's a member of, allowing me to release them right there?
Wow, guys. Thanks so much for all your responses--tragically I sent you all on a horrible goosechase.
My NavigationView navigates a NSArray of Event objects (local arts events). My table view drills down to a detail view.
My detail view has in it the following:
-(void)loadEvent:(Event *)event
{
thisEvent = event;
}
And I call that from my table view before pushing the detail view onto the nav stack. thisEvent is a synthesized property of type Event, and so since it's synthesized, I dutifully release'd it in [dealloc].
Many of you already see the problem. Backing up to the table view, when I scroll such that the one I just saw is displayed, it builds the custom table row, and so it goes to get the title property from the Event.... which I just released inside the detail controller. Boom.
I added a retain to that loadEvent: method above and the crashes, they are gone.
NONE of this was really about the views getting retained and released by the navcontroller. It was about accidentally over-releasing the data objects I'm navigating. Part of what had me discover this was, I NSLogged myself in the [dealloc] of each of these view controllers, and I can now see they're behaving exactly as they should.
Thanks! I SO love this site.
I'd guess the fault lies in EventsSearchViewController's init. Is it returning an autoreleased self by mistake ?
Looks like EventsSearchViewController is allocating an array and then over-releasing it, with one of the releases probably in its dealloc.
If you comment out the release, your EventsSearchViewController is never deallocated (it leaks). So, errors that occur as a result of its own dealloc will be masked since that method won't be called. Releasing the controller is the right thing, but you have another bug in the controller itself that only appears at dealloc time.
It could also be that dealloc is releasing an autoreleased array, so you may not have two explicit release calls in your code. But it looks very much like releasing something in dealloc that's causing the problem.
In a view based app, I display a view and once the user is finished with that view, Done is clicked and the view is removed. However, that does not dealloc the view, which is what I want to do. What is the proper way to dealloc this type of view?
Currently, I'm nil'ing out the second view before it is called. That seems to work and the second view is reinitialized. However, isn't it more appropriate for the second view to destroy itself (nil itself after removeFromSuperview)?
In first view:
//show next view
aView = nil;
if(aView == nil)
{
aView = [[AView alloc] initWithNibName:#"aView" bundle:nil];
}
[self.view addSubview: aView.view];
Click Done in aView
[self.view removeFromSuperview];
The method removeFromSuperview will automatically release "aView.view", so you shouldn't release aView in the first view controller. I think you've declared AView *aView in head file, but you don't need to. You may declare the aView as a local variable like this:
// go to second view
SecViewController *sec = [[SecViewController alloc] initWithNibName:#"SecView" bundle:nil];
[self.view addSubview:sec.view];
// go back
[self.view removeFromSuperview];
Immediately after
[self.view addSubview: aView.view];
You should add:
[aView release];
Your subview has been retained by your view controller's view so can be release.
If you previously set your viewController to first responder status (like to respond to motion events) the responder chain will retain the controller. You must resign the first responder to destroy the controller completely.
[self resignFirstResponder];
[self.view removeFromSuperview];
both of the above answers are correct theory for how you should memory manage in objective c.
as per the dev documentation:
http://developer.apple.com/iPhone/library/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instm/UIView/removeFromSuperview
callling removeFromSuperview will actually call release FOR you, so you are okay, I think.