Управление вызовами к объектам, которые освобождаются при выходе из представления - PullRequest
2 голосов
/ 22 июня 2010

У меня есть контроллер представления, управляемый в UINavigationController. Мой контроллер представления выводит экран «загрузки» и использует ASIHTTP для асинхронной загрузки данных таблицы. Затем он отображает свою таблицу, и в каждой ячейке есть изображение, которое он использует ASIHTTP для асинхронной загрузки. При загрузке каждого изображения он использует [self.tableView reloadRowsAtIndexPaths] для перезагрузки этой строки, внутри которой изображение подается в UIImageView в каждой строке.

Отлично работает. Но если я выхожу из поля зрения до того, как загрузка закончится, он вылетает.

Где происходит сбой, это мой -tableView:numberOfRowsInSection метод, и NSZombies говорит мне, что он умирает, потому что он запрашивает -count NSArray с именем self.offers, который был освобожден.

Этот метод выглядит так:

-(NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
 return [self.offers count];
}

Обтекание этого возврата в if (self.offers) не имело значения.

My -dealloc освобождает и устанавливает нулевое значение для каждого из этих свойств, включая как self.offers, так и сам self.tableView. Я даже пытался установить свойство BOOL disappearing, нажав YES в -viewWillDisappear, и отключить условное поведение, но это не работает, потому что viewWillDisappear, кажется, не вызывается! Насколько я могу судить, мы не вызываем ЛЮБОГО метода, когда навигационная панель выводит нас из строя.

Что мне с этим делать?

EDIT:

Благодаря @cduhn (который гонится за чеком!), Я сделал еще кучу, глядя на это. Проблема в том, что мой -dealloc просто не вызывается, когда я запускаю этот viewcontroller (ни мои -viewWillDisappear, ни -viewDidUnload или что-либо еще, что я мог бы использовать, чтобы отцепить структуру делегирования, которая является корнем этой проблемы).

Тогда я понял: этот viewController не тот, что в стеке NavController! На вершине стека находится представление оболочки, просто сегментированный контроллер и большой пустой UIView. Я переключаю содержимое этого UIView между двумя другими подклассами UIViewController в зависимости от состояния моего сегментированного контроллера. Поэтому, когда мое представление с таблицей в виде PARENT выскользнуло из стека навигации, этот РЕБЕНОК, над которым я работаю, похоже, не получил никакого уведомления об этом. Что странно, потому что я определенно release это делаю.

Я могу вызвать его -dealloc с моего контроллера оболочки. Я мог бы назвать его -viewWillDisappear тоже, в этом отношении. Это то, как я должен справиться с этим? Вероятно, я должен поместить что-то в представление моего контроллера оболочки, как:

[[self.mainView subviews] makeObjectsPerformSelector: @selector (viewWillDisappear)];

... чтобы сообщение распространялось на мои дочерние представления.

Думаю, я на правильном пути?

(О, боже ... и это также объясняет, почему действия из этого дочернего табличного представления не могут добраться до self.navigationController! Я недоумевал об этом в течение нескольких недель!)

Ответы [ 2 ]

4 голосов
/ 23 июня 2010

Подобные ошибки, когда метод вызывается для объекта после его освобождения, часто происходит, когда метод вызывается для делегата после того, как этот делегат был освобожден. Рекомендуемая практика, чтобы избежать подобных ошибок, состоит в том, чтобы установить любые свойства делегата (или подобного делегату) объекта равным nil перед тем, как освободить этот объект в методе dealloc делегата. Я знаю, что это запутанное предложение, поэтому я объясню его в контексте вашей ошибки.

У вас есть асинхронная загрузка изображений, которая заканчивается после того, как вы отказались от контроллера табличного представления. Когда это происходит, вы звоните reloadRowsAtIndexPaths:withRowAnimation:, что приводит к вызову tableView:numberOfRowsInSection: в табличном представлении dataSource. Этот вызов не выполняется, поскольку этот источник данных больше не существует.

Проблема в том, что объект табличного представления по-прежнему имеет ваш контроллер в качестве его свойств dataSource и delegate, даже после того, как ваш контроллер был освобожден. Решение состоит в том, чтобы просто установить эти свойства равными nil в dealloc вашего контроллера, например:

- (void)dealloc {
    self.tableView.dataSource = nil;
    self.tableView.delegate = nil;
    self.tableView = nil; // Releases as a side-effect due to @property (retain)
    [super dealloc];
}

Теперь, когда ваше табличное представление пытается вызвать tableView:numberOfRowsInSection: в своем источнике данных, оно отправит сообщение объекту nil, который молча проглатывает все сообщения в Цели C. C.

Кстати, вы также должны сделать то же самое с делегатом вашего ASIHTTPRequest.

Каждый раз, когда у вас есть асинхронная операция, которая вызывает методы делегата после завершения, особенно важно, чтобы вы установили для этого делегата значение nil. При использовании UITableViewController в нормальных условиях вы обычно можете уйти, не устанавливая dataSource и делегировать значение nil, но в этом случае это было необходимо, потому что вы вызываете методы для tableView после того, как его контроллер исчезнет.

0 голосов
/ 23 июня 2010

Насколько я могу судить, пользователь не может "отказаться" от представления, пока UITableView загружает его данные.Методы не запускаются в потоке и блокируют основной, также блокируя взаимодействие с пользовательским интерфейсом.Я не могу повторить ваши результаты.Даже, быстро прокручивая представление таблицы, а затем нажимая кнопку возврата.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...