0

I've seen various question on this topic but never found a good answer for it.

I have an App that's using UITableViewController and that instantiates a SearchDisplayController for it.

I receive random crash from my crash reporter that every time looks like

[Object _existingView]: unrecognized selector sent to instance

Object can be anything, there is absolutely no rule, except that it is always an internal object, not one from the App code.

It can affect different version of iOS up to 6.0.

It looks like a SearchDisplayController have not been deallocated and is sending rotation messages to internal objects (see crash report below)

It is really strange and the code above is the only place where I instantiate a searchDisplayController in all the project classes.

I will never post something here if I was able to reproduce this issue, but unfortunately I've not been able to reproduce it, even by trying to navigate trough dozens of UITableViewController in the App itself and sending memory warning to the simulator.

If anyone did experience this issue previously, it could be interesting and maybe we can provide a definitive answer on this topic (several post on this)

This is how the searchBar and SearchDisplayController are created:

UISearchBar *searchBar = [[[UISearchBar alloc] init] autorelease];
searchBar.barStyle = UIBarStyleBlack;
self.createdSearchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
self.searchDisplayController.searchResultsDelegate = self;
self.searchDisplayController.searchResultsDataSource = self;
self.searchDisplayController.delegate = self;
searchBar.frame = CGRectMake(0, 0, 0, 38);
self.tableView.tableHeaderView = searchBar;

And after reading some answers, I've upgraded the dealloc method to nil delegates even though it should be a weak link. This is how I'm deallocating this in the dealloc (sorry, pre-ARC code, I still have to upgrade):

[[NSNotificationCenter defaultCenter] removeObserver:self];
fetchedResultsController.delegate = nil;
self.searchDisplayController.delegate = nil;
self.searchDisplayController.searchResultsDelegate = nil;
self.searchDisplayController.searchResultsDataSource = nil;
[createdSearchDisplayController release];

I'm still unsure if the SearchDisplayController is released properly, even though I don't see any other mean to do it as searchDisplayController is a read-Only property.

Using Instruments I do not see any leak by using the leak trace but when looking at the allocation trace there, it seems the system keeps some reference to searchDiplayController, even after sending several memory warnings in the simulator.

Below the crash report, with no mention of our App, except the first statements main and start:

0 CoreFoundation 0x35b0b88f __exceptionPreprocess + 162
1 libobjc.A.dylib 0x3372f259 objc_exception_throw + 32
2 CoreFoundation 0x35b0ea9b -[NSObject doesNotRecognizeSelector:] + 174
3 CoreFoundation 0x35b0d915 ___forwarding___ + 300
4 CoreFoundation 0x35a68650 _CF_forwarding_prep_0 + 48
5 UIKit 0x334d4ebb -[UISearchDisplayController windowWillAnimateRotation:] + 126
6 Foundation 0x34f254ff __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_0 + 18
7 CoreFoundation 0x35ad7547 ___CFXNotificationPost_block_invoke_0 + 70
8 CoreFoundation 0x35a63097 _CFXNotificationPost + 1406
9 Foundation 0x34e993eb -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
10 UIKit 0x332efa57 -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:isRotating:] + 3450
11 UIKit 0x33380fa7 -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:] + 46
12 UIKit 0x33380f37 -[UIWindow _setRotatableViewOrientation:duration:force:] + 70
13 UIKit 0x3324fa01 -[UIWindow _updateToInterfaceOrientation:duration:force:] + 108
14 UIKit 0x33236cff -[UIWindow _updateInterfaceOrientationFromDeviceOrientation:] + 162
15 UIKit 0x332500c7 -[UIWindow _updateInterfaceOrientationFromDeviceOrientationIfRotationEnabled:] + 74
16 Foundation 0x34f254ff __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_0 + 18
17 CoreFoundation 0x35ad7547 ___CFXNotificationPost_block_invoke_0 + 70
18 CoreFoundation 0x35a63097 _CFXNotificationPost + 1406
19 Foundation 0x34e993eb -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
20 UIKit 0x33210deb -[UIDevice setOrientation:animated:] + 214
21 UIKit 0x3320c16f -[UIApplication handleEvent:withNewEvent:] + 2718
22 UIKit 0x3320b567 -[UIApplication sendEvent:] + 54
23 UIKit 0x3320af3b _UIApplicationHandleEvent + 5826
24 GraphicsServices 0x337fd22b PurpleEventCallback + 882
25 CoreFoundation 0x35adf523 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 38
26 CoreFoundation 0x35adf4c5 __CFRunLoopDoSource1 + 140
27 CoreFoundation 0x35ade313 __CFRunLoopRun + 1370
28 CoreFoundation 0x35a614a5 CFRunLoopRunSpecific + 300
29 CoreFoundation 0x35a6136d CFRunLoopRunInMode + 104
30 GraphicsServices 0x337fc439 GSEventRunModal + 136
31 UIKit 0x33239cd5 UIApplicationMain + 1080  
32 AppName 0x0001a1cb main + 66
33 AppName 0x00016348 start + 40

1 Answers1

0

The above code is definitely leaking but in a subtle way. You don't see it with leaks in instruments but if you track allocation in your heap, you'll see they keep piling up.

Somehow wording my question for SO gave me the answer about the deallocation issue.

Apple have a very strange behavior for creating the searchDisplayController and they made it a read-only property, all the more confusing as our standard way of doing won't work:

// This is not possible and not correct.
self.searchDisplayController = nil

But it does not mean you cannot send them a release!

// This is the only correct way of releasing a searchDisplayController from a TableView
[self.searchDisplayController release]

This should be called in 2 places:

  • in your dealloc method of course.
  • in your alloc method that creates the searchBar and the SDC.

So the correct and really leak free way of creating a searchDisplayController is:

// Alloc and create searchBar + searchDisplayController
if (self.searchDisplayController) [self.searchDisplayController release];
UISearchBar *searchBar = [[[UISearchBar alloc] init] autorelease];
searchBar.barStyle = UIBarStyleBlack;
[[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
self.searchDisplayController.searchResultsDelegate = self;
self.searchDisplayController.searchResultsDataSource = self;
self.searchDisplayController.delegate = self;
searchBar.frame = CGRectMake(0, 0, 0, 38);
self.tableView.tableHeaderView = searchBar;

// Dealloc
[NSNotificationCenter defaultCenter] removeObserver:self];
fetchedResultsController.delegate = nil;
self.searchDisplayController.delegate = nil;
self.searchDisplayController.searchResultsDelegate = nil;
self.searchDisplayController.searchResultsDataSource = nil;
[self.searchDisplayController release];

This has been verified to be heap growth 0 in instruments after several calls. And has been tested on different iOS version as well. 4.3 / 5.0 / 5.1 and 6.0

At the moment, I'm still not sure if it solves the rotation issue and unrecognized calls to _existingView but it sure corrects the issue of correctly deallocating a searchDisplayController used in a UITableViewController.

An answer to a regular question on the forum that never got correctly answered.

Off course, I'll update about the crash issue once the App is live with the fix (we cannot seem to reproduce the issue) to see if it finally solved it.

John Topley
  • 113,588
  • 46
  • 195
  • 237
  • Updated version has been live on the Store for 4 days. No more issue of existingView. So not releasing correctly SearchDisplay controllers can lead to rotation issues and unexpected crashes. Above solution is working to create and release a searchDisplayController within your App. – Chevenement David Oct 30 '12 at 18:16