iOS - Crash on [self.navigationController setToolbarHidden:YES animated:NO] Deallocated error - iphone

I have a crash on [self.navigationController setToolbarHidden:NO animated:YES] located in my viewWillAppear when I come back to the UIViewController using that toolbar. With this Error *** -[CALayer retain]: Message sent to deallocated instace 0x5d0e0a0 I am not expressly releasing the toolbar but the class where I am setting it is autoreleased.
Also am I incorrect in assuming that the toolbar is on the navigationController?

You should retain a reference to the toolbar in the navigation controller in which it appears. You should not set UI interfaces in transient classes. It is important to separate your models from the views via a controller - thus "Model-View-Controller" design pattern.
A tool bar does not necessitate a navigation controller. Do not confound with a navigation bar, which can be sort of similar.

There was a cheap and easy way to get this to work.
[self.navigationController.toolbar init];

Related

ARC UINavigationController stack not getting deallocated when presented as modal view controller

First: I ported my App to ARC and everything seemed to work. But now I discovered a problem: I have a UINavigationController that is presented modally with some UIViewControllers on its stack. But when I dismiss the modal view controller, the view controllers from the stack don't seem to be deallocated. Here is what I do:
UIViewController* root = [[UIViewController alloc] init];
UINavigationController* navi = [[UINavigationController alloc] initWithRootViewController:root];
[self presentModalViewController:navi animated:TRUE];
Then from the root I push some more view controllers, but that doesn't really matter. The fact is when I later call
[self dismissModalViewControllerAnimated:TRUE];
root doesn't get deallocated. Of course in my code root is a subclass of UIViewController, and I track dealloc and viewDidUnload, but nothing gets called.
Any ideas?
What's inside your navigation controller? It could be that something else (perhaps a view controller inside your navigation controller) is the culprit, which is leading up the chain meaning the navigation controller doesn't get released.
Either way, the code you posted is correct, so if your navigation controller isn't being released after calling dismissModalViewController it would suggest that something else still has an active reference to it or one of its dependencies. I know that doesn't answer your question, but you will probably have to hunt around.
Since you aren't showing actual code, it's hard to tell what is going on with your root view controller.
But, with ARC, if you have a strong pointer to an object, it won't get released. I suspect that you are holding on to this controller after adding it to your navigation controller.
But, without seeing your code, I can't tell.

Preventing bad access crash for popViewControllerAnimated in uinavigationcontroller setup

So, under low memory, my root view is unloaded. That's expected and accounted for. However, how do I deal with modal/pushed view controllers who need to be popped, since they reference the main navigationController? I have a custom Done button in a pushed view controller, which calls [self.navigationController popViewControllerAnimated:YES]. If the root view has been unloaded, this gives a bad access error. Is there a better way to do this?
My setup is
AppDelegate has a NavigationController
this NavigationController has a view controller MainViewController
//MainViewController.m
- (IBAction)showAnotherController:(id)sender
{
AnotherViewController * anotherViewController;
anotherViewController = [[AnotherViewController alloc] initWithNibName:#"AnotherView" bundle:nil];
[self.navigationController pushViewController:anotherViewController animated:YES];
[anotherViewController release];
}
//...Here I can simulate a memory warning to force unloading of MainViewController's view
//in AnotherViewController.m, called from a custom toolbar item
- (IBAction)done:(id)sender
{
[self.navigationController popViewControllerAnimated:YES]; // bad access here, looks like self.navigationController is no longer available. Am I doing this wrong?
}
The scenario you're describing does not cause bad access. However, if you don't handle the described scenario carefully you get bad access. Since you haven't showed me any code I can't tell you that this is the problem you're having, but it's a very common problem.
When you receive a memory warning and your view is unloaded, you probably release a bunch of stuff. In addition to releasing the variables you also have to set them to nil. If you don't, you're facing the risk of sending messages to released objects, which causes bad access.
Again, I can't know that this is your problem, but it's usually the problem.
EDIT: Since you seem to believe that self.navigationController doesn't exist (it probably does) I'm gonna tell you about something called NSZombie:
NSZombie will tell you what released object is being sent a message (aka EXC_BAD_ACCESS). This is a very useful tool when you get EXC_BAD_ACCESS, so learn how to use it.
To activate NSZombie do the following:
Get info of the executable.
Go to the arguments tab.
In the "Variables to be set in the environment:" section add:
Name: NSZombieEnabled
Value: YES
Then run your app as usual and when it crashes it should tell you which deallocated object received the message.

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.

How do I destroy a view that is added by pushViewController?

After adding a view by pushViewController method, there will be a back button in the navigation bar to pop the view off the stack. However, it seems that iOS won't destroy the view after popping it off the stack so when will it be destroyed? Can we destroy it manually when popping out the view?
Generally the pattern is like this:
- (void)pushSomeViewControllerOnStack
{
SomeViewController* someViewController = [[SomeViewController alloc] initWithNibName:#"SomeView" bundle:nil];
[self.navigationController pushViewController:someViewController animated:YES];
[someViewController release];
}
In other words, the navigation controller will do its own retain of the view controller, which means you also need to release it yourself, since there's an init. The navigation controller will also take care of releasing this controller when appropriate.
You should implement the viewDidUnload and dealloc methods within your UIViewController subclasses.
When a UINavigationController pops a view controller off its stack the code within those methods will be executed.
You should read the View Controller Programming Guide for iOS: Navigation Controllers documentation from Apple's iOS Developer Library as well as the class reference documentation for the UINavigationController and UIViewController classes so that you will better understand the view controller life cycle and what to expect when various application events occur.

UIViewController custom titleView crashes app

I have a navigation based iPhone app.
When you press on a cell in the tableview a new UIViewController is pushed to the navigation stack. In this view controller I am setting a custom titleView in the viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
// Setup custom navigation title
[self setTitle:#"Mediaportal"];
navItem = [[NavigationBarTitleItemViewController alloc] initWithNibName:#"NavigationBarTitleItem" bundle:nil];
[navItem setTitle:[theServer name]];
[navItem setSubTitle:#""];
[self.navigationItem setTitleView:navItem.view];
…
}
Once I switch back to the RootViewController:
[self.navigationController popToRootViewControllerAnimated:YES];
the app crashes with the following error (NSZombieEnabled=YES):
*** -[CALayer retain]: message sent to deallocated instance 0x5a5fd80
From what I can see the RootViewController still tries to access the custom titleView, which was deallocated with the second view controller. Once I comment out the custom titleView part in my code the app works.
I tried to set the navigationItem.titleView to nil (as found in the apple docs) before the second ViewController is deallocated, but that doesn't help.
Do you have a hint what I can do to prevent this crash?
Thanks,
Mark.
I had this exact same error a month or so ago, exactly the same situation. It drove me NUTS.
I discovered that the viewController i was popping too hadn't been deallocated at all. I had a custom UIButton subclass added to that view however that had been deallocated when the second view had been pushed. So when popping back, the UIButton wasn't there.
Check the view you are popping back to, to ensure you've not got any classes that you are deallocating, or are being autoreleased without you knowing.
Hope this helps.
I finally found the solution for it (a quite simple one).
I have to alloc and init the navItem through its property then it is being retained:
self.navItem = [[NavigationBarTitleItemViewController alloc] initWithNibName:#"NavigationBarTitleItem" bundle:nil];