Сбой при вращении SearchDisplayController: [Object _existingView]: нераспознанный селектор отправлен на экземпляр

Я видел разные вопросы на эту тему, но так и не нашел хорошего ответа на них.

У меня есть приложение, которое использует UITableViewController и создает для него экземпляр SearchDisplayController.

Я получаю случайный сбой от моего аварийного репортера, который каждый раз выглядит так

[Object _existingView]: unrecognized selector sent to instance

Объект может быть чем угодно, здесь нет абсолютно никаких правил, за исключением того, что это всегда внутренний объект, а не один из кода приложения.

Это может повлиять на разные версии iOS до 6.0.

Похоже, что SearchDisplayController не был освобожден и отправляет сообщения вращения внутренним объектам (см. Отчет о сбое ниже)

Это действительно странно, и приведенный выше код - единственное место, где я создаю экземпляр searchDisplayController во всех классах проекта.

Я никогда не буду публиковать здесь что-либо, если смогу воспроизвести эту проблему, но, к сожалению, я не смог воспроизвести это, даже пытаясь перемещаться по десяткам UITableViewController в самом приложении и посылая предупреждение памяти в симулятор.

Если кто-то сталкивался с этой проблемой ранее, это может быть интересно, и, возможно, мы сможем дать исчерпывающий ответ по этой теме (несколько постов на эту тему)

Вот как создаются searchBar и SearchDisplayController:

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;

И после прочтения некоторых ответов я обновил метод dealloc до нуля делегатов, даже если это слабое звено. Вот как я освобождаю это в dealloc (извините, код до ARC, мне все еще нужно обновить):

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

Я до сих пор не уверен, что SearchDisplayController выпущен правильно, хотя я не вижу другого способа сделать это, так как searchDisplayController является свойством только для чтения.

Использование инструментов Я не вижу утечек с помощью трассировки утечек, но, глядя на трассировку распределения, кажется, что система сохраняет некоторую ссылку на searchDiplayController, даже после отправки нескольких предупреждений памяти в симуляторе.

Ниже отчета о сбое, без упоминания нашего приложения, кроме первых утверждений main и 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 ответ

Вышеприведенный код определенно просачивается, но тонким способом. Вы не видите это с утечками в инструментах, но если вы будете отслеживать распределение в своей куче, вы увидите, что они продолжают накапливаться.

Каким-то образом формулировка моего вопроса для SO дала мне ответ о проблеме освобождения.

У Apple очень странное поведение при создании searchDisplayController, и они сделали его доступным только для чтения, что еще более запутанно, поскольку наш стандартный способ работы не сработает:

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

Но это не значит, что вы не можете отправить им релиз!

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

Это следует называть в 2 местах:

  • в вашем методе dealloc конечно.
  • в вашем методе alloc, который создает searchBar и SDC.

Таким образом, правильный и действительно бесплатный способ создания searchDisplayController:

// 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];

Это было подтверждено, чтобы быть кучи рост 0 в инструментах после нескольких вызовов. И был протестирован на разных версиях iOS. 4.3 / 5.0 / 5.1 и 6.0

На данный момент я все еще не уверен, решает ли это проблему поворота и нераспознанные вызовы _existingView, но это точно исправляет проблему правильного освобождения searchDisplayController, используемого в UITableViewController.

Ответ на обычный вопрос на форуме, который так и не получил правильного ответа.

Конечно же, я сообщу о проблеме сбоя, как только приложение исправит исправление (мы не можем воспроизвести проблему), чтобы увидеть, наконец ли оно решило ее.

Другие вопросы по тегам