Подтвердить что ты не робот

ARC, блоки и сохраняемые циклы

Работа над проектом 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, ни одно из его свойств не заполняется внутри блока.

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

4b9b3361

Ответ 1

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

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

Ответ 2

Это, по-видимому, проблема, описанная Конрадом Столлом в Блоки, операции и сохраняемые циклы, но его запись пропускает несколько важных моментов:

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

Простейшее решение выглядит так:

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

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

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

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