ARC, блоки и циклы удержания - PullRequest
28 голосов
/ 14 октября 2011

Работа над проектом iOS, нацеленным на 4.0 и 5.0, с использованием ARC.

Вхождение в проблему, связанную с блоками, ARC и ссылкой на объект из-за пределов блока.Вот некоторый код:

 __block AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
   [operation setCompletionBlock:^ {
       if ([operation isCancelled]) {
           return;
       }

... do stuff ...

operation = nil;
}];

В этом случае компилятор выдает предупреждение, что использование 'операции' в блоке приведет к циклу сохранения.В ARC __block теперь сохраняет переменную.

Если я добавлю __unsafe_unretained, компилятор немедленно освобождает объект, поэтому, очевидно, это не сработает.

Я нацеливаюсь на 4.0, чтобы я мог 'я не использовал __weak.

Я пытался сделать что-то вроде этого:

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
__block __unsafe_unretained AFHTTPRequestOperation *weakOperation = operation;

, но, хотя weakOperation не равен nil, ни одно из его свойств не заполняется внутри блока.

Как лучше всего справиться с этой ситуацией, учитывая ограничения проекта, перечисленные выше?

Ответы [ 2 ]

23 голосов
/ 14 октября 2011

Если предположить, что прогресс достигнут, цикл сохранения может быть именно тем, что вы хотите.Вы явно прерываете цикл сохранения в конце блока, так что это не постоянный цикл сохранения: когда вызывается блок, цикл прерывается.Вы можете сохранить ссылку в переменной __weak или __unsafe_unretained, а затем использовать ее из своего блока.Нет необходимости __block -квалифицировать переменную, если вам по какой-то причине не нужно менять привязку переменной во время блока;так как у вас больше нет цикла сохранения, вам не нужно ничего присваивать слабой переменной.

1 голос
/ 03 июня 2013

Кажется, это проблема, описанная Конрадом Столлом в Blocks, Operations и Retain Cycles , но в его рецензии пропущено несколько важных моментов:

  • __block выглядиткак рекомендованный Apple способ избежать сильной ссылки на захваченные переменные в режиме MRC, но совершенно не нужен в режиме ARC.В этом случае это совершенно не нужно в режиме ARC;это также не нужно в режиме MRC, хотя облегченный обходной путь гораздо более подробный: void * unretainedOperation = operation; ... ^{ AFHTTPRequestOperation * op = unretainedOperation; }
  • В режиме ARC вам необходимы как сильная ссылка (так что вы можете добавить ее в очередь), так и слабая/ unsafe_unretained reference

Самое простое решение выглядит следующим образом:

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
AFHTTPRequestOperation * __unsafe_unretained unretainedOperation = operation;

[operation setCompletionBlock:^ {
  if ([unretainedOperation isCancelled]) {
    return;
  }
  ... do stuff ...
}];

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

Лучшим исправлением представляется обновление до последней AFNetworking , которая передает операцию в блок в качестве аргумента.

...