UISearchDisplayController вызывает сбой после viewDidUnload - PullRequest
16 голосов
/ 20 декабря 2011

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

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

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

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

Я сделал обратный след и получил этот вывод:

#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. Любые идеи о том, как решить эту проблему?

ОБНОВЛЕНИЕ: Даже когда я удаляю соединение между моим контроллером представления и свойством для контроллера отображения поиска, но создаю для него собственный IBOutlet, он все равно падает. Возможно, плохая ошибка?

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

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

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

  1. Создание контроллера представления в раскадровке с UISearchDisplayController
  2. Вставить новый контроллер представления в стек навигации
  3. Вызывает предупреждение о нехватке памяти
  4. Вытащите контроллер из стека
  5. KABOOM!

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

Ответы [ 7 ]

12 голосов
/ 14 января 2012

Вот что я сделал (конечно, это обходной путь, а не исправление ошибки 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;
11 голосов
/ 24 мая 2012

Пока что я нашел это рабочее решение для iOS 5 SDK, используя ARC:

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

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

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

@synthesize searchDisplayController;

Но не установить его в ноль в viewDidUnload.

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

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

0 голосов
/ 16 апреля 2014
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;
0 голосов
/ 29 июля 2013

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

if (self.searchDisplayController.active) {

        [self.searchDisplayController setActive:NO];

}
0 голосов
/ 27 марта 2012

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

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

0 голосов
/ 16 января 2012

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

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

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

0 голосов
/ 14 января 2012

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

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

...