views:

216

answers:

2

Hi,

I have two view controllers A and B. From A, I navigate to view controller B as follows:

// in View Controller A 
// navigateToB method

-(void) navigateToB {

BViewController *bViewController = 
[[BViewController alloc] initWithNibName: @"BView" bundle:nil];

bViewController.bProperty1 = SOME_STRING_CONSTANT;
bViewController.title = @"A TITLE OF A VC's CHOOSING"; 
[self.navigationController pushViewController: bViewController animated:YES];
[bViewController release]; //<----- releasing 0x406c1e0

}

In BViewController, the property bPropery1 is defined with copy as below (note, B also contains UITableView and other properties):

@property (nonatomic, copy) NSString *bProperty1;

Everything appeared to work fine when navigating back and forth between A and B. That is until I added a UISearchDisplayController to the table view contained in BViewController. Now when I navigate out of B, back to A, the app crashes.

Stack trace shows what looks the search display controller being autoreleased at time of crash:

#0 0x009663a7 in ___forwarding___
#1 0x009426c2 in __forwarding_prep_0___
#2 0x018c8539 in -[UISearchDisplayController _destroyManagedTableView]
#3 0x018c8ea4 in -[UISearchDisplayController dealloc]
#4 0x00285ce5 in NSPopAutoreleasePool

NSZombies shows:

-[BViewController respondsToSelector:]: message sent to deallocated instance 0x406c1e0

And malloc history on this points to the bViewController already released in A's navigateToB method above:

    Call [2] [arg=132]: thread_a065e720 |start  ... <snip> 
..._sendActionsForEvents:withEvent:] | -[UIControl sendAction:to:forEvent:] | -
[UIApplication sendAction:to:from:forEvent:] | -[**AViewController navigateToB**] | 
+[NSObject alloc] | +[NSObject allocWithZone:] | _internal_class_createInstance | 
_internal_class_createInstanceFromZone | calloc | malloc_zone_calloc 

Can someone please give me any ideas on what is happening here? In navigateToB method, once the bViewController is released (after pushViewController), that's should be it for bViewController. Nothing else even knows about it as it is local to the navigateToB method block and it has been released.

When navigating from B back to A, nothing is invoked in viewDidLoad, viewWillAppear etc that will re-enter navigateToB.

It looks like somehow search display controller has a reference to something in my AViewController and so as it is autoreleased it is taking this "something" down with it but I cannot understand how this is possible, especially as I'm using copy to pass data between A and B.

I'm going potty over this. I'm sure this is my mistake somewhere and so I turn to you, Stack Overflow legends for any words of wisdom or advice on how to resolve this.

Many Thanks.

+1  A: 

When you push a view controller onto a navigation controller, the navigation controller retains the view controller. And when that view controller is popped, the navigation controller releases it.

UISearchDisplayController appears to be trying to see if your view controllers responds to a selector, and since it's calling from _destroyManagedTableView, I'm guessing the selector is searchDisplayController:willUnloadSearchResultsTableView: (your view controller is the search display controller's delegate, correct?).

@robert - that example must be wrong, because pushViewController does retain its argument. All the other examples on the page either autorelease immediately after init, or release after pushing.

Brian
Thanks Brian. The BViewController is the search display controller's delegate,though malloc history shows AViewController in the trace (i'm guessing this is because its showing where BViewController originated from?). So sounds like the UISearchDisplayController is trying to message its delegate which has been released? Do I need to somehow release / nil the UISearchDisplayController as well in BViewController's dealloc?
Tofrizer
Yes, the search display controller should be released in BViewController's dealloc (along with anything else that is created in BViewController's init, or loaded by a nib and assigned to an IBOutlet).
Brian
Thanks, seems to be working, you rock! I'll continue to test and monitor (and of course award the answer to you :) ) but I see now what is happening. BTW, reason why I earlier commented "do i need to somehow" release the UISearchDisplayController is I never created an IBOutlet for the search display controller. Originally, in IB when I drag/dropped the new UISearchDisplayController control, it seemed to automatically link with the File Owner. Now I realise, having an explicit IBOutlet allows me to release the search display controller along with its delegate view controller (BViewController).
Tofrizer
A: 

I had a similar problem solved by adding these lines to the dealloc method of my UITableViewController that was the delegate of the UISearchDisplayController:

self.searchDisplayController.delegate = nil;
self.searchDisplayController.searchResultsDelegate = nil;
self.searchDisplayController.searchResultsDataSource = nil;

I was slightly confused by the suggestion that to fix this issue the search display controller should be released in dealloc -- what you need to do is remove the pointers that the search display controller has to your table view controller since your table view controller is now going away and should not be called when your search display controller is later released. It's just like any other delegate reference that you want to nil out in dealloc.

Jeff