UIViewController -dealloc Being Called After Dismissing Modal View Controller - iphone

I have a view in my app that displays a UITableView. This view is created in a nib file and has a custom view controller. The UIViewController subclass for this view acts as the Datasource and Delegate for the UITableView.
My UITableView displays several rows based on my data. Then, the last row displays different text: "Add another...". If the last row is selected, I present a modal view controller (to allow the user to add more data). When I dismiss the modal view controller, I again see the original view (as expected) and all appears to be well. However, when I try to interact with this view, the app crashes.
From placing several NSLog() statements through the UIViewController (for the UITableView), I have determined that the -dealloc method is being called just after the modal view is dismissed. This explains the crash when I try to do something with that view. However, I have no idea why -dealloc is being called on this view controller.
To dismiss the modal view controller, I have:
[self dismissModalViewController:YES];
As the code in an IBAction method in the modal view controller's UIViewController. This action is tied to a cancel button in the corresponding nib file.
In addition, my understanding from the View Controller Programming Guide is that it's OK to dismiss the modal controller from within itself, but it's more robust to use delegates. I was initially using a delegate, but took the delegate out to simplify debugging. I just put the delegate back in to double-check, and the same behavior occurs when using delegates. The modal controller's action method calls is implemented as:
[[self delegate] myModalViewController:self didAddObject:obj];
The delegate implementation in the parent view controller is:
[self dismissModalViewController:YES]
If anyone has seen this before or has any suggestions of what could be happening or how to debug this, I would greatly appreciate it.

If -dealloc is being called, something is releasing the view controller. Try implementing -release in your view controller:
-(void)release {
NSLog(#"view controller released");
[super release];
}
so that you can use the debugger to inspect the call stack when this unexpected release message happens.

Its dangerous to call dismissModalViewController from the modal view controller itself (message will be forwarded to parent view controller), if you have not retained it elsewhere. Normally, the parent view controller is responsible for dismissing the modal view controller it presented.

Related

viewDidLoad method is calling again after setting nil to tableView in UITableViewController in iOS

I am using UITableViewController in my applicaton. While pop from tableview controller to back, i was setting nil to table view to deallocate the view controller from memory. its again calling viewDidLoad, please explain me why it is happening.
-(void)handleBackButton{
self.tableView=nil;
[self.navigationController popViewControllerAnimated:YES];
}
You could set a breakpoint in viewDidLoad and look at the stack trace to see why it's reloading its view.
But I can make an educated guess. When you tell the navigation controller to pop a view controller, it needs to animate that view controller's view off the screen. To do so, it probably asks the disappearing view controller for its view.
A UITableViewController's view is the same as its tableView. When you set tableView to nil, you also set view to nil. So when the navigation controller asks the table view controller for its view (so it can animate the view off the screen), the table view controller notices that its view is nil, so it loads its view. And to load its view, it sends itself loadView and then it sends itself viewDidLoad.
I don't know why you would really bother trying to unload the view. If the view controller itself gets deallocated, it will release its view (which will deallocate the view unless you've retained it somewhere else). And if the view controller doesn't get deallocated, isn't that usually because you might want to put its view back on the screen soon?
Anyway, if you really want to get rid of a view controller's view, don't set its view to nil while the view might still be in the on-screen view hierarchy. Wait until the view is definitely out of the hierarchy. For example, subclass UITableViewController, and override didMoveToParentViewController: like this:
- (void)didMoveToParentViewController:(UIViewController *)parentViewController {
[super didMoveToParentViewController:parentViewController];
if (parentViewController == nil) {
self.tableView = nil;
}
}

Why is self.navigationController null in viewDidLoad?

I know there are a lot of similar questions, but I can't find one that specifically addresses this.
Why is self.navigationController null when called in viewDidLoad, but correct when called from outside viewDidLoad?
This is my output from NSLog(#"%#",self.navigationController); The first is called in viewDidLoad, the second I add a button to the interface with a method that calls NSLog(#"%#",self.navigationController);:
NavApp[31524:11003] (null)
NavApp[31524:11003] <UINavigationController: 0x6e21190>
I know there must be some simple explanation for this behavior, I'm just curious as to what it is. Thanks!
A view controller's view is loaded when you first access the -view method/property on that controller. After the view has been loaded, the viewDidLoad method is called. This is pretty straight forward. You also have to remember that the view can be loaded/unloaded multiple times if memory warnings are received while the view is off-screen.
So viewDidLoad does not mean your view controller has been inserted into a navigation controller. In the process of pushing a view controller onto a navigation controller, its view will be accessed and loaded, but this will happen before the whole push is complete. So viewDidLoad is clearly getting called before the navigationController property has been updated.
You also have to consider that some other part of your code could be accessing the view controller's view before you even push the view controller onto the navigation controller.
So viewDidLoad is the wrong place to do what you are doing. You probably want to use a method like viewDidAppear: so you know that the view controller's view is part of the view hierarchy when it is invoked.

Dismissing a UIPopoverController from within its contentViewController?

If you want to dismiss a popover -- for example, from a button within the popover's contentViewController you must --
Create a reference to the popover to be held by view controller which creates it
Create a notification from the contentViewController to let the owning view controller know that it should be dismissed, or alternately create a delegate for the same purpose
Send the notification or delegate message when the popover is ready to be dismissed
Call dismissPopover:animated when the notification or delegate method is called
Meanwhile, from a UIViewController you can access the modal view controller, the parent view controller, the navigation controller, the split view controller, the tab bar controller, the search display controller, the child view controllers, the presenting view controllers, and the presented view controllers.
Is there a better approach to do this from popover's contentViewController?
Unfortunately, you'll have to create a weak property reference to said UIPopoverController as there's no way to access it from within the content view controller.
I was surprised how UIViewControllers can access the modal view controller, the parent view controller, the navigation controller, the split view controller, the tab bar controller, the search display controller, and as of iOS 5, the child view controllers as well as presenting and presented controllers...but not the popover controller (granted popovers aren't UIViewControllers but still).
Technically, there's a private, undocumented method to retrieve the popoverController that the UIViewController is in...I have no idea why they never made it public given that it should be exactly the same as any of the above controllers.
Though even in the private, undocumented world, there's no equivalent to dismissModalViewcontrollerAnimated:. You'll still have to get that reference then dismiss it that way.
Another way to solve this is to create an abstract view controller (for all your view controllers) that adds an NSNotification observer to a method such as -(void)closePopoverIfNecessary:(NSNotification*)notification and have child classes optionally implement the method to close their popover(s) if open. Then from within the popover's controller you fire the notification to close it. You could also pass other info via the notification (userInfo) if needed.
This way there's no need for the parent references.

When is viewWillAppear called?

By my count, the only two instances when viewWillAppear is called is when you initialize your view controller, or when you pop off the view controller that's on top of it on the navigation stack (ie pushing the back button on the viewcontroller ahead of it). Are there any other instances when viewWillAppar is called? I don't believe it's called when the app becomes active. Interested to hear some responses on this.
viewwillappear method is called as and when the view controller's view is added to the window. ( if the view is already in the window and is hidden by another view, this method is called when the view is once again revealed). The method is a notification to the view controller that the view is about to become visible. You can override this method to make any customizations with presenting the view.
This will also be called anytime addSubView is called, with your view.

Call method from Modal View Controller

I have a view controller which presents a modal view when a certain button is tapped. Upon closing the modal view and re-revealing the original view underneath, I want a refresh method to be called. How do I call this refresh: method in OriginalViewController from ModalViewController?
I know this works if I do it in -viewDidAppear, but I only want it to happen when the modal view closes, not every single time.
As you can see in the View Controller Programming Guide, the recommended way is to use delegation.
How do you do it is up to you, but a standard way to so would be to define a protocol such as:
#protocol RecipeAddDelegate <NSObject>
- (void)modalViewControllerDismissed:(ModalViewController *)modalViewController;
#end
Then on your OriginalViewController you can implement that method, and act when the modal view controller has been dismissed:
- (void)modalViewControllerDismissed:(ModalViewController *)modalViewController {
[self refresh]; // or anything you want to do
}
As an additional comment, the guide I linked suggest that you should dismiss the modal not from the modal itself but from the controller that opened it. In the example, they create the delegate protocol a bit different, so it has methods for the original controller to be informed of the actions the modal controller does, and be able to decide when to close it.
Have a look at the View Controller Programming Guide, specifically, the section on dismissing a modal view.
The OriginalViewController should have a protocol method called by the ModalViewController when it's done. It should be the OriginalViewControllers responsibility to dismiss the modal view and perform any tasks it needs to on itself, such as refreshing itself.