Ручной срок службы объекта с помощью ARC
Изучите следующий код и предположите, что он был скомпилирован в ARC:
- (недействительно)foo { NSOperationQueue *oq = [[NSOperationQueue alloc] init]; [oq addOperationWithBlock:^{ // Представим, что у нас здесь длительная операция. }]; }
Хотя очередь операций объявляется как локальная переменная, ее время жизни остается за рамками метода, пока в нем выполняются операции.
Как это достигается?
ОБНОВИТЬ:
Я ценю хорошо продуманные комментарии Роба Мейоффа, но думаю, что не правильно задал свой вопрос. Я не задаю конкретный вопрос о NSOperationQueue, а скорее задаю общий вопрос о времени жизни объекта в ARC. В частности, мой вопрос заключается в следующем:
Как, согласно ARC, объект может участвовать в управлении собственной жизнью?
Я был программистом в течение очень долгого времени, и я хорошо осведомлен о подводных камнях такой вещи. Я не собираюсь читать лекции о том, хорошая это или плохая идея. Я думаю, что в целом это плохо. Скорее, мой вопрос академический: хорошая это или плохая идея или нет, как можно это сделать в ARC и каков конкретный синтаксис для этого?
4 ответа
В общем случае вы можете сохранить ссылку на себя. Например:
@implementation MasterOfMyOwnDestiny
{
MasterOfMyOwnDestiny *alsoMe;
}
- (void) lifeIsGood
{
alsoMe = self;
}
- (void) woeIsMe
{
alsoMe = nil;
}
...
@end
Вот несколько возможностей:
NSOperationQueue
сохраняет себя, пока не опустеет, а затем освобождает себя.NSOperationQueue
заставляет некоторый другой объект сохранить это. Например, так какNSOperationQueue
возможно, использует GCDaddOperationWithBlock:
выглядит примерно так:- (void)addOperationWithBlock:(void (^)(void))block { void (^wrapperBlock)(void) = ^{ block(); [self executeNextBlock]; }; if (self.isCurrentlyExecuting) { [self.queuedBlocks addObject:wrapperBlock]; } else { self.isCurrentlyExecuting = YES; dispatch_async(self.dispatchQueue, wrapperBlock); } }
В этом коде
wrapperBlock
содержит сильную ссылку наNSOperationQueue
, так (предполагая ARC), он сохраняетNSOperationQueue
, (РеальныйaddOperationWithBlock:
является более сложным, чем это, потому что он потокобезопасен и поддерживает одновременное выполнение нескольких блоков.)NSOperationQueue
не живет за рамками вашегоfoo
метод. Может к тому времениaddOperationWithBlock:
возвращается, ваш длительный блок уже был отправлен в очередь GCD. Поскольку вы не держите сильную ссылку наoq
нет причин, почемуoq
не должен быть освобожден.
Самая простая вещь, о которой я могу подумать, это иметь глобальный NSMutableArray (или набор, или что-то еще), к которому объект добавляет себя и удаляет себя. Другая идея заключается в том, чтобы поместить (как вы уже признали) код, управляемый странным образом из памяти, в категорию в файле, не являющемся ARC, и просто использовать -retain и -release напрямую.
В примере кода, заданного в ARC, NSOperationQueue, будучи локальным для охватывающей лексической области видимости блока, перехватывается блоком. По сути, блок сохраняет значение указателя, чтобы к нему можно было получить доступ позже. Это на самом деле происходит независимо от того, используете вы ARC или нет; Разница в том, что в ARC объектные переменные автоматически сохраняются и освобождаются при копировании и освобождении блока.
Раздел "Объектные и блочные переменные" в руководстве " Темы программирования блоков" является хорошим справочным материалом для этого материала.