Защита моего кода от зомби от блоков завершения - PullRequest
5 голосов
/ 18 февраля 2012

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

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

Я вызываю сетевую службу с моего контроллера представления и передаю блок, который обновляет мой UITableView. При нормальных обстоятельствах работает нормально. Однако, если я оставлю представление до его завершения, будет выполнен блок обработчика завершения - но теперь UITableView - зомби.

Каков обычный способ обращения с этим?

ОБНОВЛЕНИЕ С ОБРАЗЦОМ КОДА

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

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

Если я уберу __block и передам себя, то он падает с зомби.

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

__block UITableView *theTable = [self.table retain];
__block IndexedDictionary *tableData = [self.descriptionKeyValues retain];
FavouritesController *favourites = [Container controllerWithClass:FavouritesController.class];
[favourites countFavouritesForPhoto:self.photo 
                         completion:^(int favesCount) {

                             [tableData insertObject:[NSString stringWithFormat:@"%i", favesCount]   
                                              forKey:@"Favourites:" atIndex:1];                 
                             [theTable reloadData];

                             [tableData release];
                             [theTable release];
                         }];

Есть советы? Спасибо

ВТОРОЕ ОБНОВЛЕНИЕ

Я изменил способ загрузки избранного. Вместо того, чтобы избранное было одиночным, я создаю экземпляр при каждом изменении фотографии. Заменив это и убив старый - блок не имеет возможности для обратного вызова (я полагаю, он даже не существует), и мой код теперь выглядит так, как показано ниже, и похоже, что он работает:

[self.favourites countFavouritesForPhoto:self.photo 
                         completion:^(int favesCount) {      
                             [self.descriptionKeyValues insertObject:[NSString stringWithFormat:@"%i", favesCount]   
                                              forKey:@"Favourites:" atIndex:1];                 
                             [self.table reloadData];
                         }];

Он не протекает и, похоже, тоже не падает.

Ответы [ 3 ]

0 голосов
/ 18 февраля 2012

Блок сохранит любой объект, на который он ссылается, кроме помеченных __block. Если вы вообще не хотите выполнять блоки завершения, просто установите какое-либо свойство, например isCancelled, и проверьте, является ли оно YES, перед вызовом блока завершения.

0 голосов
/ 18 февраля 2012

Итак, у вас есть фоновая операция, которая должна вызвать другой объект после его завершения, и объект может быть уничтожен за это время.Сбои, которые вы описываете, происходят, когда у вас есть не сохраненные ссылки.Проблема, как вы видите, состоит в том, что указанный объект исчезает, а указатель недействителен.Обычно вы отменяете регистрацию делегата внутри метода dealloc, чтобы фоновая задача продолжалась, и всякий раз, когда она готова сообщить результаты, она говорит: « Shoot, мой объект обратного вызова равен nil », и впо крайней мере, это не дает сбоя.

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

Если вы ориентируетесь на iOS 5.0, я бы прочитал ARC и слабые ссылки, которые он предоставляет.Если вы не хотите использовать ARC или вам нужно ориентироваться на устройства до 5.x, я бы рекомендовал использовать обнуление слабых справочных библиотек, таких как MAZeroingWeakRef , которые также работают для устройств 3.x.

Используя слабые ссылки ARC или MAZeroingWeakRef, вы выполняете фоновую задачу с помощью одного из этих причудливых слабых ссылочных объектов, указывающих на вашу таблицу.Теперь, если заостренный объект исчезнет, ​​слабый указатель обнулится и ваше фоновое задание не будет аварийно завершено.

0 голосов
/ 18 февраля 2012

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

Сохранение UITableView в блоке - плохая идея, потому что обновления источника данных / представления таблицы могут привести к неявным вызовам методов и уведомлениям, которые не будут релевантными, если представление таблицы не отображается на экране.

...