Правильное использование блоков Objective-C с ARC?

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

Сильное сохранение себя в этом блоке может привести к циклу удержания

- (void)viewDidLoad {
   [super viewDidLoad];

   self.webClient.completionHandler = ^{
      [self populateData];
   };
}

Я попытался сделать что-то вроде кода ниже, и я все еще получаю то же предупреждение. Какое решение?

__weak id myself = self;
[myself populateData];  

person aryaxt    schedule 26.06.2012    source источник
comment
Вы помещаете строку __weak внутрь блока? Если это так, это должно быть перед строкой, в которой вы устанавливаете обработчик завершения/определяете блок.   -  person UIAdam    schedule 26.06.2012
comment
да прямо перед [self populateData]   -  person aryaxt    schedule 26.06.2012
comment
Есть ли более чистый способ сделать все внутри блока?   -  person aryaxt    schedule 26.06.2012
comment
Что, если вы измените self.webClient... только на webClient...?   -  person jjv360    schedule 26.06.2012
comment
Неважно, используете ли вы свойство для доступа к webClient или нет. webClient предположительно является сильным иваром (то есть сохраняется самим собой), поэтому, если self сохраняет webClient и webClient сохраняет блок, а блок сохраняет self, у вас есть цикл сохранения.   -  person UIAdam    schedule 26.06.2012
comment
UIAdam, чтобы уточнить, вы говорите, что доступ к ivar в блоке приводит к тому, что блок сохраняет self?   -  person andyvn22    schedule 26.06.2012
comment
Что ж, я отредактировал свой последний комментарий, чтобы он больше соответствовал комментарию @ jjv360, но да, если вы получите доступ к ivar в блоке, блок сохранит себя. В данном случае это не совсем проблема, поскольку вы напрямую используете self в блоке.   -  person UIAdam    schedule 26.06.2012


Ответы (2)


Ваш код должен выглядеть так:

- (void)viewDidLoad {
   [super viewDidLoad];

   __weak id weakSelf = self;
   self.webClient.completionHandler = ^{
      [weakSelf populateData];
   };
}
person UIAdam    schedule 26.06.2012

UIAdam дал правильный ответ, но стоит понять, почему он правильный.

Во-первых, почему вы получили предупреждение?

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

Попытка написания

__weak id myself = self;
[myself populateData];  

не работает конечно. Блок по-прежнему ссылается на себя, потому что присваивает его себе. Так что тут никакой разницы.

Решение UIAdam для письма

__weak id weakSelf = self; self.webClient.completionHandler = ^{ [weakSelf populateData]; };

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

Что, если это произойдет, но что-то еще имеет сильную ссылку на webClient и вызывается ваш блок? weakSelf — это слабая ссылка, и слабые ссылки обнуляются при освобождении объекта. Поэтому вы должны быть готовы к тому, что weakSelf будет равен нулю, когда ваш блок будет вызван. Вообще лучше написать

id strongSelf = weakSelf;
[strongSelf populatedData];

внутри блока: strongSelf может быть установлен в nil, или он будет установлен в self. Но поскольку это сильная ссылка, она останется отличной от нуля, пока блок не завершится. Если бы не было нуля с самого начала.

person gnasher729    schedule 03.04.2014
comment
На самом деле лучше написать В данном случае разницы нет. - person newacct; 04.04.2014