Preventing bad access crash for popViewControllerAnimated in uinavigationcontroller setup - iphone

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.

Related

Very strange behaviour of dealloc - not getting called

I have tested my all viewControllers dealloc methods. And all of them getting called properly on calling popViewControllerAnimated.
But only 1 controller's dealloc method not getting called. I am not able to figure out the issue.
While pushing to that controller I have properly written following code:
AController *contr = [AController alloc]initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:contr animated:YES];
[contr release];
and when I am coming back from controller I have written :
[self.navigationController popViewControllerAnimated:YES];
This is really strange behaviour because this code is written on many controllers and its working properly.
If it's not getting called it's still alive. Try to use instruments to find it. If you use the allocations tool in instruments you should be able to find the class (by name) in a list of allocations and see if it is still alive or not. You can even see by whom (I'm pretending that classes are people) it is retained.
If dealloc is not called you might have another object that retain it.
Check that object that might use this delegate do not retain it.
Hi I know this is an old post, but this answer may help someone stuck in my position. Spent a long time trying to find out why dealloc wasn't getting called. It turned out that I was not invalidating an NSTimer in my viewWillDisappear method and hence it was holding on to the retain count.
This great blog post is a must read for people in this situation:
http://www.reigndesign.com/blog/debugging-retain-cycles-in-objective-c-four-likely-culprits/#comment-41302
I was having the similar issue;I wanted to show a UIViewController for like 3 seconds(copyright screen). So I was essentially calling the PushViewCOntroller and popViewController from the main file and the dealloc was not getting called when i was popping the view controller.
I then switched from pushViewCOntroller to
[self.navigationController presentModalViewController:copyrightView animated:NO];
and
[self.navigationController dismissModalViewControllerAnimated:NO];
and it started working.
I dont know how it can fix the issue; but it did.
For me using autorelease when allocating viewcontroller worked hope this helps someone
[[AController alloc]initWithNibName:nil bundle:nil]autorelease];
In my case, i have assigned circular ref object type with strong reference. Changing to weak type fixed this issue.
Try getting which object retained in memory using Instruments as said in other answers.

iPhone How To Save View States

I have been developing iphone applications for around 3months now and theres a few things that stump me and i don't really have an idea how to work round them.
I have a navigation controller controlling the views in my application however every screen that is loaded, used then pushed back loses all the information as it seems to be reinstantiated... I believe this is a possible memory management issue?
But how to i create an app that navigates and retains all information in its views until the application is closed.
Thanks :)
Possible you didn't keep a reference to the view controller, the issue is for UIVIewController not to be released.
Make the view controller an ivar you will instanciate only one time when you push it on stack.
// in .h
MyViewController *mVC;
// in .m
// maybe when the user selects a row in a tableview
if(mVC == nil) {
// first time use, alloc/init
mVC = [[MyViewController ....];
}
// then push on the stack
[self.navigationController ....];
Of course don't forget to release it later.
In this part:
MyViewController *myViewController=[MyViewController alloc] initWithNibName:#"myView" bundle:nil];
[[self navigationController] pushViewController:myViewController animated:YES];
[myViewController release];
You will probably have something like this... Instead, make your myViewController a class's property so you have a reference to it. And drop the [myViewController release]; statement.
Possibly your app is receiving a didReceiveMemoryWarning.
In such cases, when the super class is called, the framework does memory cleaning by unloading all the views that are not currently displayed. This could explain the behavior you are seeing.
To check it further, override didReceiveMemoryWarning in one of your view controllers or applicationDidReceiveMemoryWarning in your app delegate, and put a breakpoint in it. Don't forget to call [super...] appropriately, otherwise pretty soon your app will be killed. What you should see in this way is that the views do not disappear before hitting the breakpoint, and do disappear after that.
If the hypothesis is correct, you should find a way to save the state of your view in viewDidUnload and restore it in viewDidLoad. Look also at didReceiveMemoryWarning reference.
Try to save data in NSUserDefaults it its small or use plist or it its too small like 5-10 objects save in in some variable in appDelegate, and if its too large use sqlite and for saving something like images of files like xml use Document directory
The UINavigationController works like a stack: you push and pop UIViewControllers on it. That means when a UIViewController get popped, it will have its retain count decremented by 1, and if no other object holds a reference to it, it will be deallocated. You can avoid the UIViewControllers getting dealloced by keeping a reference to them yourself by calling -retain on the objects, for instance in your appDelegate.
You can use NSUserDefaults to save the states of the UIControls in the view.
So whenever u r loading a view, set the values to the controls so that it looks like it resume from the place where we left.

"[CALayer release]: message sent to deallocated instance" when dismissing modal view controller

I've been struggling with this for last few days and I cannot find any solution, so I ask you for advice.
I have two UIViewControllers: NewPostUIViewController and SettingsUIViewController. In the second one I have a field:
id<SettingsUIViewControllerDelegate> delegate
and the first one implements protocol
SettingsUIViewControllerDelegate
When a button is pressed the following code is executed in NewPostUIViewController:
SettingsUIViewController *settingsUIViewController = [[SettingsUIViewController alloc] initWithNibName:#"SettingsView" bundle:nil];
settingsUIViewController.title = NSLocalizedString(#"Settings", #"Settings view title");
settingsUIViewController.delegate = self;
[self presentModalViewController:settingsUIViewController animated:YES];
[settingsUIViewController release];
when I want to dismiss SettingsUIViewController I call (code in SettingsUIViewController):
[delegate settingsAreDone:sender];
and settingsAreDone looks following (code in NewPostUIViewController):
[self dismissModalViewControllerAnimated:YES];
This all concludes in:
[CALayer release]: message sent to deallocated instance 0x5a76840
I tried to debug the code by setting a breakpoint in the release methods of both view controllers, but these methods are called so often that it's hard to say what can be the cause of this problem.
Any ideas?
First, the error you're getting isn't indicating that -release is being sent to a view controller, so breakpoints in your view controllers won't help. The over-release is happening on a CALayer, which is likely part of the modal animation.
First, we start with some basics about the delegate. I don't feel great about this being the cause, but you should always start with the easy basics. Your SettingsUIViewController delegate property should be assign, not retain, so you avoid retain loops. That's probably correct already, but when it's not, you can wind up with cases where objects exist longer than you expect them to (and so can send messages after their targets have gone away). Again, probably not the issue, but easy to check and easy to fix.
Next, you should look at the stack trace at the crash. Who is calling [CALayer release]? A possible cause is that the owning view controller gets released before the animation stops. When you close the settings controller, do you immediately close the NewPost controller?

popToRootViewController crashing

I am a relatively new iPhone app developer so my knowledge is a little sketchy, so please forgive me if this is a bit of a trivial question.
I have a navigation app which drills between table views by calling pushViewController on the navigationController object
I have one particular section which pushes new view controllers sequentially as the user goes through the interface. The view controllers are all subclassed from UIViewController.
It all works fine until it gets to the end, where the idea is the user presses a "Finish" button and is returned to the root view controller (main menu).
So on the button press I call:
[[self navigationController] popToRootViewControllerAnimated:YES];
And it crashes.
I am a bit worried this could be a big problem as this definitly worked at some point but it is now always failing.
Can anyone give any ideas/advice?
Some suggestions:
Before calling popToRootViewControllerAnimated: confirm that the RootViewController does actually exist. If it died somewhere along the line, calling the method will cause a crash.
Check the – viewWillDisappear: and – viewDidDisappear: methods of your last view to make sure you're not doing something dangerous there.
Not sure if popping a view causes it to always deallocate but check the dealloc method of the views and their controllers to make sure your not over-releasing something.
One mistake I've seen a lot is releasing objects in the data model from controllers. When another controller (in this case the RootViewController) tries to access the data model the app crashes.
It sound's like you need how to use the Xcode debugger. Type in debugger in Xcode help to get pointers.
You should not be using popToRootViewController in your viewWillDisappear.
Instead if you want to pop to root controller on your pressing the back button, you should replace the back button by your own and add an action to it. Try doing something like ::
UIBarButtonItem *back = [[UIBarButtonItem alloc] initWithTitle:#"back"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(goBack:)];
self.navigationItem.leftBarButtonItem = back;
and then handle the action as ::
- (void) goBack:(id)sender
{
// pop to root view controller
[self.navigationController popToRootViewControllerAnimated:YES];
}
As the others have commented, the first step is to run this is debug mode and figure out where and why you are crashing.
The most common type of crash is using a deallocated object (EXEC_BAD_ACCESS). Have you run the static analyzer? Are you properly retaining your object references?

how to know whether dealloc is getting called or not in iphone sdk?

i am using UIviewcontroller subclasses. In my main view i have 3 buttons, each button will load a different nib. and each new nib is having one back button to come back to main view.
when i click one the back button of any view to move to the main view the dealloc of that view is not getting called? i didnt understood this.
can anyone explain when those views dealloc will be called?
if the dealloc method hasn't been called, it means that your retained your viewController object by hands. for example, in this case dealloc will not be called after clicking back button to return
MyViewController *controller = [[MyViewController alloc] init];
[self.navigationController pushViewController:controller animated:YES];
You should add
[controller release];
to this code to be sure that your instance of viewController will be deallocated. If you are absolutely sure, that you had sent equal number of alloc(or any message that increases object's retainCount) and release messages for your object and dealloc method doesn't be called anyway, it will be more complex. I hope that this answer will help. If you will find that your situation is "more complex", post a comment, then I'll try to explain with more details.
I too would like to dive deeper into understanding memory management details (below surface level) where it comes to controllers being pushed on and off of the stack. I built my framework from the text, "Beginning iPhone 3 Development" by Mark and LaMarche, but that text effectively re-uses sub-controllers and their dealloc methods never get called.
I have noticed that repeated use of a sub-controller with a NIB containing a UIWebView that calls Google's web directions url ... eventually results in a memory warning and my data is lost. This involves repeated "reuse" of the sub-controller.
If you can point me as well to in depth text that goes into nav controller and sub view memory management, that would be excellent.