Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UISearchDisplayController causes crash after viewDidUnload

I have a project using StoryBoards and UISearchDisplayController used in the context of a UINavigationController, that appears in the root viewcontroller. When I push a new view controller onto the stack and I cause a simulated memory warning (or actually get a low memory warning). The previous view controller successfully unloads its view. However, when I pop the second view controller off of the stack I get an EXC_BAD_ACCESS. I turned on NSZombies and discovered this:

[UISearchDisplayController retain]: message sent to deallocated instance 0xb13aa30

I am not (at least in my code) sending that message to the UISearchDisplayController. I am not doing anything, programmatically speaking, with it. Break points reveal that I am not even making it into the viewDidLoad of the first view controller.

Something curious, though: for laughs and giggles I decided to outright retain the SDC in my viewDidLoad, just to see what would happen and no crash occurs. However, my UISearchDisplayController instance is nil.

I did a backtrace and get this output:

#0  0x01e30e1e in ___forwarding___ ()
#1  0x01e30ce2 in __forwarding_prep_0___ ()
#2  0x01dd1490 in CFRetain ()
#3  0x01eb69c0 in +[__NSArrayI __new::] ()
#4  0x01e0a00a in -[__NSPlaceholderArray initWithObjects:count:] ()
#5  0x01e34f52 in +[NSArray arrayWithObjects:count:] ()
#6  0x01e5e084 in -[NSDictionary allValues] ()
#7  0x01035272 in -[UINib instantiateWithOwner:options:] ()
#8  0x00edce2c in -[UIViewController _loadViewFromNibNamed:bundle:] ()
#9  0x00edd3a9 in -[UIViewController loadView] ()
#10 0x00edd5cb in -[UIViewController view] ()
#11 0x00edd941 in -[UIViewController contentScrollView] ()
#12 0x00eef47d in -[UINavigationController _computeAndApplyScrollContentInsetDeltaForViewController:] ()
#13 0x00eef66f in -[UINavigationController _layoutViewController:] ()
#14 0x00eef93b in -[UINavigationController _startTransition:fromViewController:toViewController:] ()
#15 0x00ef03df in -[UINavigationController _startDeferredTransitionIfNeeded] ()
#16 0x00ef16cb in _popViewControllerNormal ()
#17 0x00ef196c in -[UINavigationController _popViewControllerWithTransition:allowPoppingLast:] ()
#18 0x0b446e82 in -[UINavigationControllerAccessibility(SafeCategory) _popViewControllerWithTransition:allowPoppingLast:] ()
#19 0x00ef0b10 in -[UINavigationController popViewControllerAnimated:] ()
#20 0x00ef297d in -[UINavigationController navigationBar:shouldPopItem:] ()
#21 0x00e7dabe in -[UINavigationBar _popNavigationItemWithTransition:] ()
#22 0x00e7da49 in -[UINavigationBar popNavigationItemAnimated:] ()
#23 0x0b42208c in -[UINavigationBarAccessibility(SafeCategory) popNavigationItemAnimated:] ()
#24 0x00e80507 in -[UINavigationBar _handleMouseUpAtPoint:] ()
#25 0x00e8074c in -[UINavigationBar touchesEnded:withEvent:] ()
#26 0x00e3fa30 in -[UIWindow _sendTouchesForEvent:] ()
#27 0x00e3fc56 in -[UIWindow sendEvent:] ()
#28 0x00e26384 in -[UIApplication sendEvent:] ()
#29 0x00e19aa9 in _UIApplicationHandleEvent ()
#30 0x02d37fa9 in PurpleEventCallback ()
#31 0x01e9e1c5 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ ()
#32 0x01e03022 in __CFRunLoopDoSource1 ()
#33 0x01e0190a in __CFRunLoopRun ()
#34 0x01e00db4 in CFRunLoopRunSpecific ()
#35 0x01e00ccb in CFRunLoopRunInMode ()
#36 0x02d36879 in GSEventRunModal ()
#37 0x02d3693e in GSEventRun ()
#38 0x00e17a9b in UIApplicationMain ()
#39 0x00002b72 in main (argc=1, argv=0xbffff620)

There doesn't appear to be anything really interesting there (is there ever? :P) and appears to be all internals to Apple's stuff. Any ideas on how to get this problem to go away?

UPDATE: Even when I remove the connection between my view controller and the property for the Search Display Controller but create my own IBOutlet for it, it still crashes. Bad bug perhaps?

UPDATE 2: When I programmatically create my own instance of a UISearchDisplayController (not through the storyboard) and create it in viewDidLoad, everything works the way it is supposed to.

UPDATE 3: I am able to consistently reproduce this problem in a new project with a storyboard. I did the same thing using a vanilla nib and everything worked the way it was suppose to. However, if I setup the same thing using a storyboard and segue, it blows up just like it does in my real project. :(

RECAP: Here are the steps in recreating this issue:

  1. Create a view controller in a storyboard with a UISearchDisplayController
  2. Push a new view controller on the navigation stack
  3. Cause a low memory warning
  4. Pop the controller off of the stack
  5. KABOOM!

viewDidLoad does not even get called on the first view controller at this point, Apple's code blows up before then.

like image 897
Wayne Hartman Avatar asked Dec 19 '11 21:12

Wayne Hartman


2 Answers

Here is what I did (granted, it's a workaround and not a fix for Apple's bug):

First, in a base UIViewController I created a property called searchController:

@property (nonatomic, retain) IBOutlet UISearchDisplayController* searchController;

I added a UISearchBar in through interface builder so that I have a placeholder in my UI for it. Then, in my viewDidLoad I manually setup the controller and wire it up:

UISearchDisplayController* searchController = [[UISearchDisplayController alloc] 
                             initWithSearchBar:self.searchBar contentsController:self];
searchController.searchResultsDataSource = self;
searchController.searchResultsDelegate = self;
searchController.delegate = self;

self.searchController = searchController;
[searchController release];

In my viewDidUnload I make sure to clear it out:

self.searchController = nil;
like image 186
Wayne Hartman Avatar answered Nov 01 '22 01:11

Wayne Hartman


So far, I found this working solution for iOS 5 SDK using ARC:

in .h file, declare your own searchDisplayController property with IBOutlet

@property (strong, nonatomic) IBOutlet UISearchDisplayController * searchDisplayController;

Then in the .m file, synthesize it:

@synthesize searchDisplayController;

But don't set it to nil in viewDidUnload.

So that the search display controller will use the property you create instead of using the inherited property.

I also notice the similar bug also appear for gesture recognizers (if you create gesture recognizers from the storyboard instead of creating them programmablly). We also need to create STRONG gesture recognizer properties and hook them with the gesture recognizer objects that you create in storyboard. Then in viewDidUnload, don't set them to nil. <-- this prevent the crashes.

like image 39
Wayne Liu Avatar answered Nov 01 '22 03:11

Wayne Liu