views:

55

answers:

1

I have a TabView application with a tab that has a NavView as one of it's views. This view has a sub view with a TableView to hold events. I've enabled the "Add" button on the top right of the nav bar and assigned the IBAction to the button.

The objective is to show a page to add a new event which is defined by a NIB file loaded when I instantiate an instance of my subclassed controller class. I'm also passing down the managedObjectContext from the parent nav controller.

This works the first time, but the second time I push the "Add" button I get an EXC_BAD_ACCESS error. I've debugged through to the push of the controller onto the nav controller and I've confirmed this is where the exception occurs. I've read through the docs and it says that a nav controller will pop a controller by hitting the "back" button supplied at the top of the nav bar. I also know that an unhandled exception will occur if I try to push the same view onto the stack. I even tried popping back to root just before the push to clear the stack but still get the exception on the push the second time around.

Should I be popping this manually (i.e. the nav "back" button is not actually popping this) somewhere else? I've confirmed also that the instance of both the nav controller and the newly instantiated view controller are not empty.

Here's the code snippet for the Add Button:

- (IBAction) addEvent: (id)sender {

 // Here we'd instantiate an instance of our Add Event Controller to show the form that allows us to enter a new event.

 // We'd add the context to the class from here so that it can get to our Core Data


  EventEntryViewController *fvController = [[EventEntryViewController alloc] initWithNibName:@"AddEventView" bundle:nil];

 fvController.managedObjectContext = self.managedObjectContext;

 [self.navigationController pushViewController:fvController animated:YES];

 [fvController release];

 fvController = nil;

}

Thanks for any help.

Update:

OK, I've stopped the exception from occurring. I tried a very simple view with nothing in it and no IBOutlets or IBActions and a controller class that had no attributes. That worked so I figured it had to be a problem with my EventEntryViewController. It didn't do much and a back trace showed it was dying on the internals of showing the view so I wasn't even getting to the loadView method let alone any other part of my code. I had a few class attributes that had been initialized in the previous load and as a good citizen I released them in viewDidUnload() and deallocated them in dealloc(). When I commented out the dealloc of my class attributes it worked!

I was confused by this and put NSLog() lines in viewDidLoad(), viewDidUnload() and dalloc(). Here's the results of the two push sequences after I took out the dallocs of my class attributes:

2010-09-18 20:17:46.224 myFuel[6435:207] EventTableNavViewController: viewDidAppear
2010-09-18 20:17:51.391 myFuel[6435:207] **EventEntryViewController**: View Did Load // loading up my class
2010-09-18 20:17:53.954 myFuel[6435:207] EventTableNavViewController: viewWillAppear
2010-09-18 20:17:54.314 myFuel[6435:207] EventTableNavViewController: viewDidAppear
2010-09-18 20:17:54.315 myFuel[6435:207] **EventEntryViewController**: dalloc       // after the class should have been popped
2010-09-18 20:18:02.803 myFuel[6435:207] **EventEntryViewController**: View Did Load //loading up my class
2010-09-18 20:18:08.134 myFuel[6435:207] EventTableNavViewController: viewWillAppear
2010-09-18 20:18:08.494 myFuel[6435:207] EventTableNavViewController: viewDidAppear
2010-09-18 20:18:08.495 myFuel[6435:207] **EventEntryViewController**: dalloc    // after the class should have been popped

Am I missing something here? Shouldn't the view have viewDidUnload() called thereby releasing the class attributes? I can see dealloc being called but where do I release these?

A: 

A couple of things based on what you've said, though I'm not clear exactly what you were doing.

First, viewDidUnload: is not guaranteed to get called. In general it will only get called if your view controller unloads it's view in response to a memory warning. As a result anything you retain references to in viewDidLoad: or any IBOutlets hooked up should get released in viewDidUnload: AND in dealloc.

Second, you say you "deallocated them in dealloc" in reference to your "class attributes" which I take to mean you are actually calling dealloc on other objects. If so ... DO NOT DO THAT. You must only call release on objects you have retained and will rarely ever have an instance when you are calling dealloc directly. This is because dealloc will be called within an object's implementation of release when it is released for the last time. That is the whole point of the retain/release system.

imaginaryboy
Thanks. I've been reading about memory management in Objective-C and specifically in the iOS framework. I had to dig into the synapses about reference counting in the old MS COM world for the lightbulb to go off. Just to be clear, in normal operations the dealloc is called and my release (if it's the last reference of the object) would get picked up GC. On a memory warning, the viewDidUnload would get called and the releases there would allow GC to clean things up. Question is would viewDidUnload AND dealloc get called in this last situation and the subsequent release cause an exception?
mcgski
When you release within `viewDidUnload:` just be sure to also set the reference to `nil`. Then when calling `release` within your `dealloc` you won't be calling release on a dangling reference. So for example if you had a `IBOutlet UILabel* label`, from 'viewDidUnload:` you would execute: `[label release]; label = nil;` and the same code in your `dealloc`. As you know, you can send a message to a `nil` reference safely, which is why the release in `dealloc` is safe.
imaginaryboy