Как может автозагрузка UISearchDisplayController вызывать сбои в другом контроллере представления?

У меня есть два контроллера вида A и B. Из A я просматриваю контроллер B следующим образом:

// 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 CHOOSING"; 
[self.navigationController pushViewController: bViewController animated:YES];
[bViewController release]; //<----- releasing 0x406c1e0

}

В BViewController свойство bPropery1 определяется с копией, как показано ниже (примечание, B также содержит UITableView и другие свойства):

@property (nonatomic, copy) NSString *bProperty1;

Все оказалось нормально работать при навигации между A и B. Это до тех пор, пока я не добавил UISearchDisplayController в представление таблицы, содержащееся в BViewController. Теперь, когда я перехожу из B, обратно в A, приложение вылетает.

Трассировка стека показывает, что выглядит, когда контроллер отображения поиска автореализован во время сбоя:

#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 показывает:

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

И история malloc по этому вопросу указывает на bViewController, уже выпущенный в методе navigateToB выше:

    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 

Может кто-нибудь, пожалуйста, дайте мне какие-нибудь идеи о том, что здесь происходит? В методе navigateToB, как только bViewController освобождается (после pushViewController), это должно быть для bViewController. Ничто другое не знает об этом, поскольку оно является локальным для блока метода navigateToB, и оно было выпущено.

При переходе с B обратно на A ничего не вызывается в viewDidLoad, viewWillAppear и т.д., который будет повторно вводить navigateToB.

Похоже, что какой-то поисковый контроллер отображает ссылку на что-то в моем AViewController, и, поскольку он автореализован, он воспринимает это "что-то", но я не могу понять, как это возможно, тем более, что я использую копию для передачи данных между A и B.

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

Большое спасибо.

Ответ 1

Когда вы нажимаете контроллер вида на контроллер навигации, навигационный контроллер сохраняет контроллер вида. И когда этот контроллер просмотра выскочил, контроллер навигации освобождает его.

UISearchDisplayController, похоже, пытается увидеть, реагируют ли ваши контроллеры представлений на селектор, и поскольку он вызывает из _destroyManagedTableView, я предполагаю, что селектор - это searchDisplayController: willUnloadSearchResultsTableView: (ваш контроллер представления является делегатом контроллера отображения, правильно?).

@robert - этот пример должен быть неправильным, потому что pushViewController сохраняет свой аргумент. Все остальные примеры на странице либо автоматически регистрируются сразу после init, либо освобождаются после нажатия.

Ответ 2

У меня была аналогичная проблема, решаясь путем добавления этих строк к методу dealloc моего UITableViewController, который был делегатом UISearchDisplayController:

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

Меня немного смутило предложение о том, что для устранения этой проблемы контроллер отображения поиска должен быть выпущен в dealloc - вам нужно удалить указатели, которые контроллер отображения отображает на ваш контроллер табличного представления, так как ваш вид таблицы контроллер теперь уходит и его не следует вызывать, когда ваш контроллер дисплея поиска будет выпущен позднее. Это точно так же, как и любая другая ссылка делегата, которую вы хотите использовать в dealloc.

Ответ 3

У меня была такая же проблема. SearchDisplayController не был создан в dealloc контроллера View, в котором он был использован. Вместо этого делегат управлял выпуском searchDisplayController, и это было сделано после того, как был выпущен tableView.  Теперь после ручного ввода этого кода в диспетчере просмотра dealloc его работа теперь прекрасна.

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

Спасибо за вашу помощь.