Ссылка на объект NSOperation в своем собственном блоке завершения с ARC
Мне сложно преобразовать код NSOperation в ARC. Мой объект операции использует блок завершения, который, в свою очередь, содержит блок GCD, который обновляет пользовательский интерфейс в основном потоке. Поскольку я ссылаюсь на свой рабочий объект изнутри собственного блока завершения, я использую указатель __weak, чтобы избежать утечки памяти. Однако указатель уже установлен на нуль к моменту моего кода.
Я сузил его до этого образца кода. Кто-нибудь знает, где я поступил не так, и верный способ выполнить это?
NSOperationSubclass *operation = [[NSOperationSubclass alloc] init];
__weak NSOperationSubclass *weakOperation = operation;
[operation setCompletionBlock:^{
dispatch_async( dispatch_get_main_queue(), ^{
// fails the check
NSAssert( weakOperation != nil, @"pointer is nil" );
...
});
}];
Ответы
Ответ 1
Я не уверен в этом, но правильный способ сделать это, возможно, добавить __block к рассматриваемой переменной, а затем установить его в конец в конце блока, чтобы убедиться, что он выпущен. См. этот вопрос.
Ваш новый код будет выглядеть так:
NSOperationSubclass *operation = [[NSOperationSubclass alloc] init];
__block NSOperationSubclass *weakOperation = operation;
[operation setCompletionBlock:^{
dispatch_async( dispatch_get_main_queue(), ^{
// fails the check
NSAssert( weakOperation != nil, @"pointer is nil" );
...
weakOperation = nil;
});
}];
Ответ 2
Другой вариант:
NSOperationSubclass *operation = [[NSOperationSubclass alloc] init];
__weak NSOperationSubclass *weakOperation = operation;
[operation setCompletionBlock:^{
NSOperationSubclass *strongOperation = weakOperation;
dispatch_async(dispatch_get_main_queue(), ^{
assert(strongOperation != nil);
...
});
}];
[operationQueue addOperation:operation];
Я предполагаю, что вы также добавите объект операции в NSOperationQueue. В этом случае очередь сохраняет операцию. Вероятно, это также сохраняется во время выполнения блока завершения (хотя я не нашел официального подтверждения о блоке завершения).
Но внутри блока завершения создается еще один блок. Этот блок будет запущен в какой-то момент позже, возможно, после завершения блока завершения NSOperations. Когда это произойдет, operation
будет освобожден в очереди, а weakOperation
будет nil
. Но если мы создадим еще одну сильную ссылку на один и тот же объект из блока завершения операции, мы убедимся, что operation
будет существовать при запуске второго блока и избежать цикла сохранения, потому что мы не фиксируем переменную operation
блоком.
Apple предоставляет этот пример в Переход к заметкам о выпуске ARC, см. последний фрагмент кода в разделе "Использовать пожизненные классификаторы для исключения сильных ссылочных циклов".
Ответ 3
Принятый ответ правильный. Однако нет необходимости ослаблять работу с iOS 8/Mac OS 10.10:
цитата из Документация NSOperation на @completionBlock:
В iOS 8 и более поздних версиях и OS X v10.10 и более поздних версиях это свойство устанавливается равным nil после начала выполнения блока завершения.
См. также этот твит от Пете Штайнбергера.