Сильный захват "себя" в этом блоке может привести к сохранению цикла

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

"Сильный захват" себя "в этом блоке может привести к сохранению цикла"

__weak typeof(self) weakSelf = self;
[generalInstaImage setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:data[@"images"][@"low_resolution"][@"url"]]] placeholderImage:[UIImage imageNamed:@"Default"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
    NSLog(@"success");
    [generalInstaImage setImage: image];
    [weakSelf saveImage:generalInstaImage.image withName:data[@"id"]];

    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
        NSLog(@"fail");
}];

Я попробую пример напиши как weakSelf.generalInstaImage, но затем компилятор генерирует ошибку и не компилируется.

2 ответа

Решение

Учтите это предупреждение:

Захватив self сильно в этом блоке, вероятно, приведет к сохранению цикла

Когда вы получите вышеупомянутое предупреждение, вы должны проверить свой блок на:

  • любые явные ссылки на self; или же
  • любые неявные ссылки на self вызвано ссылками на любые переменные экземпляра.

Давайте представим, что у нас есть какое-то простое свойство класса, которое было блоком (в нем будут появляться те же предупреждения о "цикле сохранения", что и в вашем вопросе, но мои примеры будут немного проще):

@property (nonatomic, copy) void (^block)(void);

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

@property (nonatomic, strong) NSString *someString;

Если вы ссылаетесь self в блоке (в моем примере ниже, в процессе доступа к этому свойству) вы, очевидно, получите это предупреждение о риске сохранения цикла:

self.block = ^{
    NSLog(@"%@", self.someString);
};

Это исправляется с помощью предложенного вами шаблона, а именно:

__weak typeof(self) weakSelf = self;

self.block = ^{
    NSLog(@"%@", weakSelf.someString);
};

Менее очевидно, вы также получите предупреждение "сохранить цикл", если вы ссылаетесь на переменную экземпляра класса внутри блока, например:

self.block = ^{
    NSLog(@"%@", _someString);
};

Это потому что _someString переменная экземпляра содержит неявную ссылку на self и на самом деле эквивалентно:

self.block = ^{
    NSLog(@"%@", self->_someString);
};

Вы можете быть склонны попытаться принять здесь также слабую модель себя, но вы не можете. Если вы попытаетесь weakSelf->_someString Синтаксический шаблон, компилятор предупредит вас об этом:

Разыменование __weak указатель недопустим из-за возможного нулевого значения, вызванного условием гонки, присвойте его strong переменная первая

Поэтому вы решаете это с помощью weakSelf шаблон, но и создать местный strong переменную внутри блока и использовать ее для разыменования переменной экземпляра:

__weak typeof(self) weakSelf = self;

self.block = ^{
    __strong typeof(self) strongSelf = weakSelf;

    if (strongSelf) {
        NSLog(@"%@", strongSelf->_someString);

        // or better, just use the property
        //
        // NSLog(@"%@", strongSelf.someString);
    }
};

Кроме того, это создание местного strong ссылка, strongSelf внутри блока есть и другие преимущества, а именно: если блок завершения работает асинхронно в другом потоке, вам не нужно беспокоиться о self освобождение во время выполнения блока, что приводит к непредвиденным последствиям.

это weakSelf / strongSelf шаблон очень полезен, когда имеешь дело со свойствами блока, и ты хочешь предотвратить сохранение циклов (или сильных эталонных циклов), но в то же время гарантируя, что self не может быть освобожден в середине выполнения блока завершения.

К вашему сведению, Apple обсуждает этот шаблон в обсуждении "нетривиальных циклов" далее в разделе " Использование квалификаторов на протяжении всей жизни для избежания сильных эталонных циклов " в примечаниях к выпуску " Переход к ARC".


Вы сообщаете, что получили какую-то "ошибку", когда ссылались weakSelf.generalInstaImage в вашем примере. Это правильный способ устранения этого предупреждения "сохранить цикл", поэтому, если вы получили какое-то предупреждение, вы должны поделиться им с нами, а также показать нам, как вы объявили свойство.

Использование __unsafe_unretained typeof(self) weakSelf = self

Другие вопросы по тегам