Ручной срок службы объекта с помощью 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

Вот несколько возможностей:

  1. NSOperationQueue сохраняет себя, пока не опустеет, а затем освобождает себя.

  2. NSOperationQueue заставляет некоторый другой объект сохранить это. Например, так как NSOperationQueue возможно, использует GCD addOperationWithBlock: выглядит примерно так:

    - (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: является более сложным, чем это, потому что он потокобезопасен и поддерживает одновременное выполнение нескольких блоков.)

  3. NSOperationQueue не живет за рамками вашего foo метод. Может к тому времени addOperationWithBlock: возвращается, ваш длительный блок уже был отправлен в очередь GCD. Поскольку вы не держите сильную ссылку на oqнет причин, почему oq не должен быть освобожден.

Самая простая вещь, о которой я могу подумать, это иметь глобальный NSMutableArray (или набор, или что-то еще), к которому объект добавляет себя и удаляет себя. Другая идея заключается в том, чтобы поместить (как вы уже признали) код, управляемый странным образом из памяти, в категорию в файле, не являющемся ARC, и просто использовать -retain и -release напрямую.

В примере кода, заданного в ARC, NSOperationQueue, будучи локальным для охватывающей лексической области видимости блока, перехватывается блоком. По сути, блок сохраняет значение указателя, чтобы к нему можно было получить доступ позже. Это на самом деле происходит независимо от того, используете вы ARC или нет; Разница в том, что в ARC объектные переменные автоматически сохраняются и освобождаются при копировании и освобождении блока.

Раздел "Объектные и блочные переменные" в руководстве " Темы программирования блоков" является хорошим справочным материалом для этого материала.

Другие вопросы по тегам