UISearchDisplayController вызывает сбой после viewDidUnload

У меня есть проект с использованием StoryBoards и UISearchDisplayController, используемый в контексте UINavigationController, который появляется в корневом диспетчере представлений. Когда я нажимаю новый контроллер представления в стек, и я вызываю симулированное предупреждение о памяти (или фактически получаю предупреждение о низкой памяти). Предыдущий контроллер просмотра успешно выгружает его представление. Однако, когда я выталкиваю второй контроллер представления из стека, я получаю EXC_BAD_ACCESS. Я включил NSZombies и обнаружил это:

[UISearchDisplayController сохранить]: сообщение отправлено на освобожденный экземпляр 0xb13aa30

Я не (по крайней мере, в своем коде) отправил это сообщение в UISearchDisplayController. Я ничего не делаю, программно говоря, с этим. Точки перехвата показывают, что я даже не делаю его в viewDidLoad первого контроллера представления.

Что-то любопытное, хотя: для смеха и хихиканья я решил прямо в моем viewDidLoad, просто чтобы посмотреть, что произойдет и не произойдет сбой. Однако мой UISearchDisplayController экземпляр nil.

Я сделал backtrace и получил этот результат:

#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)

Там, кажется, нет ничего действительно интересного (есть ли когда-нибудь?: P) и, кажется, все внутренние элементы для Apple. Любые идеи о том, как заставить эту проблему уйти?

UPDATE: Даже когда я удаляю соединение между моим контроллером представления и свойством для контроллера отображения поиска, но создаю для него свой IBOutlet, он все равно сбой. Возможно, ошибка?

ОБНОВЛЕНИЕ 2: Когда я программно создаю свой собственный экземпляр UISearchDisplayController (а не через раскадровку) и создаю его в viewDidLoad, все работает так, как предполагается.

ОБНОВЛЕНИЕ 3: Я могу последовательно воспроизводить эту проблему в новом проекте с раскадрой. Я сделал то же самое, используя ванильный наконечник, и все работало так, как предполагалось. Однако, если я настраиваю одно и то же с помощью раскадровки и segue, он взрывается так же, как в моем реальном проекте.: (

RECAP. Ниже приведены шаги по воссозданию этой проблемы:

  • Создайте контроллер просмотра в раскадровке с помощью UISearchDisplayController
  • Нажмите новый контроллер просмотра в стеке навигации
  • Причина предупреждения о низкой памяти
  • Выключите контроллер из стека
  • KABOOM!

viewDidLoad даже не вызывает вызов на первом контроллере представления в этот момент, код Apple взорвется до этого.

Ответы

Ответ 1

Вот что я сделал (предоставил, это обходное решение, а не исправление ошибки Apple):

Во-первых, в базе UIViewController я создал свойство с именем searchController:

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

Я добавил конструктор UISearchBar в интерфейсный интерфейс, чтобы у меня был заполнитель в моем пользовательском интерфейсе. Затем, в моем viewDidLoad, я вручную настраиваю контроллер и подключаю его:

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

self.searchController = searchController;
[searchController release];

В моем viewDidUnload я обязательно очищу его:

self.searchController = nil;

Ответ 2

До сих пор я нашел это рабочее решение для iOS 5 SDK с помощью ARC:

в .h файле, объявите свой собственный объект searchDisplayController с IBOutlet

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

Затем в файле .m синтезируйте его:

@synthesize searchDisplayController;

Но не установить значение nil в viewDidUnload.

Чтобы контроллер отображения поиска использовал созданное вами свойство вместо использования унаследованного свойства.

Я также замечаю, что подобная ошибка также появляется для распознавателей жестов (если вы создаете распознаватели жестов из раскадровки, а не создаете их программно). Нам также нужно создать свойства распознавания жестов STRONG и подключить их к объектам распознавания жестов, которые вы создаете в раскадровке. Затем в viewDidUnload не устанавливайте их в nil. < - это предотвращает сбои.

Ответ 3

Почему вы не используете self.searchDisplayController только?

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

Ответ 4

@Wayne: Я столкнулся с той же проблемой с SearchDisplayController, созданным из раскадровки, и потратил на день попытку отладки сбой, который, казалось, появился, когда ни один из моих кодов не работал. В моем случае симптомом был пользователь, который вносит вкладку в UITabBarController, чтобы вернуться в ViewController, который был выгружен после предупреждения о памяти. Невыраженный метод просмотра ViewDidLoad контроллера просмотра никогда не запускается, и код получает по крайней мере до tabBarController: didSelectViewController: (который должен запускаться после viewDidLoad), прежде чем он сбой где-нибудь в коде сборки!

Большое спасибо за публикацию этого обходного пути и за все последующие действия. Небольшое улучшение заключается в том, чтобы перенести ваш экземпляр UIDisplayController на лениво загруженный метод доступа для свойства searchDisplayController. Практический эффект ничтожно мал, но он выглядит лучше!

Ответ 5

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

if (self.searchDisplayController.active) {

        [self.searchDisplayController setActive:NO];

}

Ответ 6

Explicitly declare your outlet 

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


Then in dealloc - add these lines - nil out the delegate / data source so that they do not receive any message further when the searchDisplayController deallocates itself.

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

Ответ 7

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

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

Потому что в коде нет ничего плохого. Когда происходят исключения памяти, тогда лучше взять предыдущий экран пользователя, сказав [self.navigationController popViewControllerAnimated:NO]