阻止可能会导致保留周期

时间:2013-05-08 21:40:14

标签: ios objective-c objective-c-blocks nsoperationqueue

我为NSOperationBlock

撰写了以下类别
@implementation NSOperationQueue (Extensions)

-(void)addAsynchronousOperationWithBlock:(void (^)(block))operationBlock
{
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    block signal = ^ {
        dispatch_semaphore_signal(semaphore);
    };

    [self addOperationWithBlock:^{
        operationBlock(signal);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_release(semaphore);
    }];
}

@end

它似乎工作正常但是当我调用它时(如下面的代码段所示)我收到警告:

  

阻止可能导致保留周期

[_queue addAsynchronousOperationWithBlock:^(block signal) {
        [self foo:nil];
         signal();
}];

foo是使用此类别的类的方法。

addOperationWithBlock:(来自NSOperationQueue)的相同代码未显示警告:

[_queue addOperationWithBlock:^ {
        [self foo:nil];
}];

我真的不明白。 特别是我不明白的是: 我应该在两种情况下实际使用弱指针吗?实际上,如果我不使用弱指针,两个片段会带来保留周期吗?

3 个答案:

答案 0 :(得分:9)

在块中使用self时,块会捕获它,而可能会导致保留周期。当self(或其具有强引用的内容)具有对块的强引用时发生循环。要避免潜在的循环,请声明一个弱指针并在块中使用它:

YourClassName * __weak weakSelf = self;

[_queue addAsynchronousOperationWithBlock:^(block signal) {
    [weakSelf foo:nil];
}];

答案 1 :(得分:4)

jszumski的答案本质上是正确的,但重要的是让“弱自我”舞蹈的形式正确。表格(建立在他的代码上)是:

YourClassName * __weak weakSelf = self;

[_queue addAsynchronousOperationWithBlock:^(block signal) {
    YourClassName * strongSelf = weakSelf;
    if (strongSelf)
         [weakSelf foo:nil];
}];

因此,我们通过强引用捕获weakSelf。如果你不这样做,weakSelf可能会在你使用它时不再存在(因为你对它的引用很弱)。

请参阅我的书中的舞蹈,以及关于由块引起的潜在保留周期的其他事项:

http://www.apeth.com/iOSBook/ch12.html#_unusual_memory_management_situations

答案 2 :(得分:4)

提炼其他人写的内容:

  1. 这两个代码示例都没有创建一个持久的保留周期 这会束缚记忆。
  2. Xcode抱怨您的addAsynchronousOperationWithBlock方法,因为它有可疑名称。它不会抱怨addOperationWithBlock,因为它有addOperationWithBlock的特殊知识可以覆盖其怀疑。
  3. 要删除警告,请使用__weak(请参阅jszumski和matt的答案)或将addAsynchronousOperationWithBlock重命名为“add”或“set”。
  4. 详细说明:

    1. 如果self拥有_queue,您将有一个短暂的保留周期。 self将拥有_queue,这将拥有块,而调用[self foo:]的块将拥有self。但是一旦块完成运行,_queue将释放它们,并且周期将被打破。

    2. 静态分析器已编程为怀疑以“set”和“add”开头的方法名称。这些名称表明该方法可以永久保留传递的块,可能创建永久保留周期。因此关于你的方法的警告。它并没有抱怨-[NSOperationQueue addOperationWithBlock:],因为有人知道NSOperationQueue在运行它们后会释放阻塞,所以不会抱怨它。

    3. 如果您使用__weak,分析师不会抱怨,因为不会有保留周期的可能性。如果您重命名您的方法,分析仪将不会抱怨,因为它没有任何理由怀疑您的方法永久保留传递给它的块。