Подтвердить что ты не робот

Поп-контроллер для корневого просмотра без сбоя анимации для представления таблицы

У меня есть 3 контроллера вида в контроллере панели вкладок. Нажатие на любую вкладку загружает свой контроллер корневого представления в стек навигации.

например. tab1, tab2 и tab3.
Второй контроллер представления в стеке навигации (tab2VC2) имеет таблицуView. Нажмите на tab2 show VC в tab2, затем нажмите на tab1, попытайтесь перейти к его rootVC. Затем приложение рушится, говоря

[UserDetailVC Tableview: cellForRowAtIndexPath]: сообщение, отправленное на освобожденный экземпляр 0xe0a23b0

Если я popToRootVC с анимацией, тогда все в порядке. Я обнаружил, что viewDidAppear в tab2VC2 вызывается, где вызывается tableView.reloadData, а затем dealloac, похоже, что reloadData начинает работать, таблица освобождается. в случае анимации он получает некоторое время, поэтому он не сбой. Но без анимации он рушится. Как вы думаете, это ошибка iPhone? или я поступаю неправильно? Поскольку у контроллера pop до root есть опция без анимации, она должна работать, а не?

#pragma mark Tab bar controller delegate
- (void)tabBarController:(UITabBarController *)tbController didSelectViewController:(UIViewController *)viewController {
    int i = tbController.selectedIndex;
    NSArray *mycontrollers = tbController.viewControllers;
    [[mycontrollers objectAtIndex:i] popToRootViewControllerAnimated:NO];
}
4b9b3361

Ответ 1

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

Здесь я думаю, что происходит под капотом. У вас есть UITableViewController, позвольте мне его myTable, в стеке UINavigationController, и этот стек навигации скрыт, потому что он на невыбранной вкладке или что-то еще. Затем вы вызываете [myTable.tableView reloadData], и iOS умело оптимизирует, не перезагружая данные сразу, потому что пользователь не увидит его в любом случае, если он будет скрыт. Вместо этого запрос перезагрузки откладывается и хранится где-то, когда отображается представление. Но прежде, чем это будет показано, вы вытащите myTable из стека навигации. Когда отображается вкладка оригинала myTable, запрос на перезагрузку запускается, но его источник данных больше не существует, поэтому это плохой доступ.

Теперь из моих тестов с подклассом UITableViewController, который использует автоматически предоставленное свойство tableView (не загружается из файла NIB), UITableView не освобождается, когда myTable освобождается, как в ситуации выше. Это было бы хорошо, за исключением того, что реализация dealloc по умолчанию для UITableViewController не очищает свойство dataSource UITableView (которое было установлено реализацией init по умолчанию).

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

- (void)dealloc {
  ...
  self.tableView.delegate = nil;
  self.tableView.dataSource = nil;
  [super dealloc];
}

Любая дополнительная мудрость была бы очень желанной.

Ответ 2

Ответ Джесси работает отлично. Я просто сделал небольшую модификацию для поддержки ARC

- (void)dealloc 
{
  self.tableView.delegate = nil;
  self.tableView.dataSource = nil;
}

Ответ 3

Дважды щелкните свой исполняемый файл в списке "Группы &"; вкладку "Аргументы"; нажмите "+" в нижней панели, введите "NSZombieEnabled", установите значение "YES", установите флажок, нажмите "Красная точка", чтобы убрать.  Теперь запустите тестовый пример, и он скажет вам, какой объект был освобожден.  Я подозреваю, что массив поддерживает ваш tableView в tab2V2.  Убедитесь, что правильная обработка памяти (правильно ли она сохранена и выпущена?).

Ответ 4

Вы можете попробовать это, чтобы избежать проблемы:

-(void)viewWillAppear:(BOOL)animated
{
   if (animated)
   {
      [self.tableView reloadData];
   }
}

При навигации с контроллера панели вкладок представление не будет анимировано. Поэтому, если представление появляется только на мгновение перед появлением в корне, оно не будет пытаться перезагрузить данные таблицы.

Ответ 5

У нас была аналогичная проблема, и я уверен, что она имеет какое-то отношение к новому SDK, если честно. Следующий код работал нормально. У меня есть один диспетчер навигации и новый, который подталкивается к нему с тремя различными видами, которые на него накладываются (подумайте: сменив пароль, когда у вас есть 3 разных шага: управление, изменение, повтор.) Если пользователь вводит неверный пароль, вы сначала быть нажата "сменить пароль контроллера", а затем быть нажата на первую страницу (вышла из системы).

Это работало до:

-(void)logout {
    [self.presentedViewController dismissModalViewControllerAnimated:TRUE];

    self.localNavigationController = nil;

    [self.navigationController popToRootViewControllerAnimated:TRUE];
 }

Как вы могли бы решить это, измените ViewWillAppear на ViewDIDAppear. У вас может быть некоторый лагг, но по крайней мере он не сбой:)

Ответ 6

В то время как ответ Джесси дал представление о семантике вокруг этой проблемы, в моем случае основная причина была другой. Просто подумал, что я упомянул об этом, если кто-то окажется в той же ситуации.

Я видел сообщение об ошибке

* - [UIAnimator removeAnimationsForTarget:]: сообщение отправлено на освобожденный экземпляр...

где рассматриваемый объект был UITableViewCell. При проверке в моем коде не было проблем с распределением памяти.

Проблема оказалась в том, что я вызывал одно из семейств методов popViewController:animated: из потока, отличного от UI. Этот поток был создан для выполнения некоторых сетевых операций и задач обработки базы данных. Я просто переместил вызов метода pop в поток пользовательского интерфейса и обнаружил, что я прекратил получение ошибки.