Слабые ссылки внутри блока
Я использую NSOperationQueue
и размещаюсь в очереди NSOperationBlocks
. Теперь блоки имеют сильную ссылку на любые экземпляры в блоке, а вызывающий объект также сильно удерживает блок, поэтому было рекомендовано сделать что-то вроде следующего:
__weak Cell *weakSelf = self;
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
UIImage *image = /* render some image */
/* what if by the time I get here self no longer exists? */
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[weakSelf setImageViewImage:image];
}];
}];
[self.renderQueue addOperation:op];
Итак, на мой вопрос, скажем, что к тому времени, когда изображение закончит рендеринг и эта строка вернется, объект Cell
больше не существует (он был освобожден, возможно, из-за повторного использования соты, что немного сложно формализовать). Когда я перейду к доступу [weakSelf setImageViewImage:]
, это приведет к ошибке EXC_BAD_ACCESS
?
В настоящее время я пытаюсь выяснить причину моей проблемы, и я думаю, что это может быть связано с этим.
Ответы
Ответ 1
Итак, __weak
является слабым этапом обнуления. Это означает, что во время вашей операции self
действительно может быть освобожден, но все слабые ссылки на него (а именно weakSelf
) будут обнулены. Это означает, что [weakSelf setImageViewImage:image]
просто отправляет сообщение в nil
, что безопасно; или, по крайней мере, он не должен вызывать EXC_BAD_ACCESS
. (Кстати, если вы квалифицировали weakSelf
как __unsafe_unretained
, вы могли бы отправить сообщения освобожденному объекту.)
Итак, я сомневаюсь, что отправка сообщения в ссылку __weak
вызывает сбой. Если вы хотите, чтобы self
сохранялся на протяжении всей вашей операции, вы можете получить сильную ссылку на слабый в области блока:
__weak Cell *weakSelf = self;
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
Cell *strongSelf = weakSelf; // object pointers are implicitly __strong
// strongSelf will survive the duration of this operation.
// carry on.
}];
Ответ 2
Если вы не используете слабый, вы создаете цикл сохранения, но цикл будет разорван, как только блоки закончат выполнение. Я, вероятно, не стал бы использовать слабых здесь.
В любом случае вы можете отправить любое сообщение в nil
, и оно будет проигнорировано. Поэтому, если переменная weakSelf
получает значение nil
, потому что объект Cell
освобождается, сообщение setImageViewImage:
будет молча делать ничего. Это не сработает.
Поскольку вы упоминаете повторное использование ячеек, я предполагаю, что ваш Cell
является подклассом UITableViewCell
. В этом случае ваш примерный код имеет серьезную проблему. UITableViewCell
обычно не освобождаются. Они помещаются в очередь повторного использования соты. Таким образом, ваша переменная weakSelf
не будет обнулена, потому что слабая ссылка только обнуляется, когда объект фактически освобождается.
К моменту окончания строки [weakSelf setImageViewImage:image]
ячейка может быть повторно использована для представления другой строки в вашей таблице, и вы помещаете неправильное изображение в ячейку. Вы должны переместить код рендеринга изображения из класса Cell
в класс данных datasource таблицы:
- (void)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Cell *cell = // get a cell...
[self startLoadingImageForIndexPath:indexPath];
return cell;
}
- (void)startLoadingImageForIndexPath:(NSIndexPath *)indexPath {
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
UIImage *image = [self renderImageForIndexPath:indexPath];
dispatch_async(dispatch_get_main_queue(), ^{
Cell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
[cell setImageViewImage:image];
});
}];
[self.renderQueue addOperation:op];
}