UIPopoverController and memory management - iphone

UIPopoverController *historyPop = [[UIPopoverController alloc] initWithContentViewController:nav];
[nav release];
[historyPop setPopoverContentSize:CGSizeMake(400, 500)];
[historyPop presentPopoverFromRect:CGRectMake(button.frame.origin.x, button.frame.origin.y, button.frame.size.width, 5) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
//[historyPop release];
This is my current code, however the Analyser says that the is probably a leak, which there is (as the release line is commented out). But if I uncomment the release line then the app crashes and says dealloc was reach on the popover while it is still visible, so when exactly should I be releasing the popover controller?

As mentioned in several places, the methods that present a popover (either from a rect, or from a toolbar button) do not retain the popover. So, your presenting view controller needs to hold a reference to it and release it at the appropriate time.
You can do this by setting the presenting view controller as the popover's delegate, as mentioned. A simpler, if slightly less memory-efficient, approach is to declare a retain property to hold the UIPopoverController. When you create the popover you assign it to the property, which retains it. If you later create another popover, it will release the previous popover when you reassign the property. Don't forget to release the property in the presenting view controller's dealloc method (as well as viewDidUnload).
This approach won't leak, and you don't need to deal with delegates. But, you will potentially keep a UIPopoverController object around longer than necessary. It's up to you to determine if that's a concern for your app.

Try autoreleasing the popover: [historyPop autorelease]. presentPopoverFromRect does not retain the popover, so autorelease won't work here. You need to setup your class as a delegate of the popover controller, and release the popover in popoverControllerDidDismissPopover:.

Related

i don't know why this table view code crashes

hello i'm studying iOS programming
i created a project, which is an empty application
and i created table view controller without xib file.
and i inserted follow code in didFinishLaunchingWithOptions
TableViewController *tvc = [[TableViewController alloc] init];
[self.window addSubView:tvc.view];
[tvc release];
this code was crashed when i scrolled down. why is that?
when i comment this code
[tvc release];
program doesn't crash.
i didn't write dealloc in AppDelegate file.
why is that??
i think i created table view controller with alloc
so retain count is 1.
and add sub view to window and table view controller retain count is 2.
so i release table view controller
but it crash when i scrolled down.
i don't know why..
help me please
Simple, adding tvc.view as a subview of the window causes tvc.view to be retained but does not retain tvc itself. In essence, your TableViewController instance becomes invalid as soon as you call release on it. The app crashes when you scroll presumably because the TableViewController instance is configured as a delegate or datasource for a UITableView or UIScrollView or any other thing associated with tvc.view.
Also note that the way you are displaying the view is not the recommended way to go about it. Really you should be calling presentModalViewController: or pushViewController: and passing the TableViewController instance. This will cause the TableViewController to be retained until it is dismissed/popped, making it safe for you to call release as in your example code.
Or, since you are doing this setup manually as part of didFinishLaunchingWithOptions, you can also set window.rootViewController directly, though again that's not really recommended. XCode allows you to specify the app's default/root view controller and will automatically set it up for you when the app launches.
Actually you have just added the view alone. So the view alone will be retained. The viewcontroller will be released. But the viewcontroller needs to be the datasource and delegate for the tableview. Since it has been deallocated, that datasource will not have any valid reference and so it crashes.

Release UIViewController if available

Hey,
I'm adding a UIViewcontroller as a subview of my current view. With something like that:
[self.view addSubview:viewcontroller.view];
The user can interact with that added viewcontroller so I can't just release it after I added it's view as my subview, right ?
So I would like to release it the dealloc methode and set it to nil in viewDidUnload when my master viewcontroller gets unloaded, right ?
The problem is, the viewcontoller I add as a subview is not added every time. So the question is, how can I ceck if the viewcontroller was added as subview and if so, release it.
Thx a lot !
Sebastian
You can check it like this:
if(viewController)
{
[viewController release];
viewController=nil;
}
and yes u need to put this in your dealloc method.
If you read correctly your code:
[self.view addSubview:viewcontroller.view];
it is not the controller which is being added as a subview, rather the view it manages.
This is an important point, since it means that the controller itself does not get retained. What is retained is the view.
So you can simply go on releasing your viewController in -dealloc as usual:
-(void)dealloc {
[viewController release]; viewController = nil;
...
}
and in your viewController's dealloc you will need to release the managed view, if you allocated it manually (or wherever it makes sense for your controller to release its view, if necessary).
On the other hand, whenever your superview is deallocated, then also the view you added as a subview will be released (as per Apple spec of addSubview behavior). So nothing to worry about here. Just release the viewController properly (and make that the view controller manages correctly tis own view).
One more note: you should not release your view controller in viewDidUnload.
In some open source code that I used to contribute to, we had a macro called RELEASE_TO_NIL which did exactly this.
#define RELEASE_TO_NIL(obj) if(obj != nil) { [obj release]; obj = nil; }
You would use it like this:
RELEASE_TO_NIL(viewController);
Simple as that.
Just add an tag to viewController.view and test if self.view contains that tag.
UIView *testView = [self.view viewWithTag:yourTag];
if(testView!=nil){
doStuff;
}
According to the Apple docs, a UIViewController instance should manage a view that fills the whole screen. If your view does not fill the screen, you could either subclass UIView to handle delegation or make the superview work as a delegate.
Also, the subviews of a view are contained in an instance of NSArray, do [myView subviews] to return this property. Ask the returned array if it contains the subview in question and you can release accordingly.
But, without knowing more, it does sound like you need to rethink how you're setting all this up.

Should pushViewController release oldController when a new one is pushed?

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.

Releasing UIViewController when not in use

Hi I hope somebody can help me with this problem.
I have a UIViewController named "Login" (for example) and when the user has successfully logged in this will call another controller to replace the "Login" controller like below:
[self presentModalViewController:anotherController animated:YES].
I do not need the previous controller any more so I placed a [self release] as shown in the code snippet below.
LoginController.m
- (void)viewDidDisappear:(BOOL)animated {
[self release];
}
This will then call the LoginController's dealloc method and I can be sure it's released.
Now in the new controller that is now in view has a button which calls a UINavigationController like below:
[self presentModalViewController:settingsNavigationController animated:YES];
But this crashes the app which would normally work if I didn't release the previous LoginController.
There is probably an easier or more logical method to release the controller but as I am running out of ideas I sometimes use drastic measures.
Thank you.
You should not be releasing the LoginController, at least not in its own -viewDidDisappear:. That controller is still in use and can be referenced, for example by the navigation controller's parentViewController property. Release the controller when it is no longer part of your view controller hierarchy, not just when it is no longer visible.
In addition [self release] is a warning sign that you are applying incorrect memory management.
1) Replace the "Login" controller
presentModalViewController doesn't replace your login view controller, but it puts anotherController on top of your login view controller.
2) viewDidDisappear
You should read documentation. Quote - You can override this method to perform additional tasks associated with dismissing or hiding the view. If you override this method, you must call super at some point in your implementation.
In other words, you must call [super viewDidDisappear:animated] too.
3) Memory Management
You should definitely need to read Memory Management Guide - http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/MemoryMgmt/MemoryMgmt.html
4) View Controller
You should definitely need to read View Controller Programming Guide too - http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html
Two problems with what you are doing.
First, viewDidDisappear is NOT a destructor. Do not release self there. viewDidDisAppear and appear are used for visibility of the view, not in/out of memory.
Second, presentModalViewController is intended for presenting a MODAL view controller, ie child.
You should consider setting up a UINavigationController and calling
[navigationController popToRootViewControllerAnimated:FALSE];
[navigationController pushViewController:(UIViewController*)controller animated:TRUE];
also in some situations you can get away with an [autorelease] view controller using present modal.

release viewcontroller after presenting modally

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.