使用ARC在其自己的完成块中引用NSOperation对象

Mar*_*eau 12 objective-c nsoperation grand-central-dispatch objective-c-blocks automatic-ref-counting

我将一些NSOperation代码转换为ARC时遇到了困难.我的操作对象使用完成块,该完成块又包含一个GCD块,用于更新主线程上的UI.因为我从自己的完成块中引用了我的操作对象,所以我使用__weak指针来避免内存泄漏.但是,在我的代码运行时,指针已经设置为nil.

我把它缩小到这个代码示例.谁知道我哪里出错了,以及正确的方法来实现这一目标?

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" );

        ...
    });
}];
Run Code Online (Sandbox Code Playgroud)

eof*_*ter 16

另一种选择是:

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];
Run Code Online (Sandbox Code Playgroud)

我假设您还将操作对象添加到NSOperationQueue.在这种情况下,队列保留操作.它可能在执行完成块期间保留它(尽管我还没有找到关于完成块的官方确认).

但是在完成块内部会创建另一个块.该块将在稍后的某个时间点运行,可能在NSOperations的完成块运行结束之后.当发生这种情况时,operation将由队列释放weakOperation并将nil.但是如果我们从操作的完成块创建另一个对同一对象的强引用,我们将确保operation在第二个块运行时存在,并避免保留周期,因为我们不operation通过块捕获变量.

Apple在转换为ARC发行说明中提供了此示例,请参阅" 使用生命周期限定符以避免强引用周期"部分中的最后一个代码段.

  • +1这是正确的答案."NSOperation"保留了完成块,因此在完成块中使用弱引用是安全的,因为它保证仍然存活.然而,OP的问题在于它们在第二个块中使用它,这在稍后执行,并且弱引用不能保证在那里存活.正确的解决方案是让第二个块强烈引用"NSOperation" (4认同)

lee*_*ker 10

我不确定这一点,但正确的方法是将__block添加到有问题的变量中,然后在块的末尾将其设置为nil以确保它被释放.看到这个问题.

您的新代码如下所示:

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;
    });

}];
Run Code Online (Sandbox Code Playgroud)


Sta*_*ich 5

接受的答案是正确的。但从 iOS 8 / Mac OS 10.10 开始无需弱化操作:

NSOperation 文档中关于 @completionBlock 的引用:

在 iOS 8 及更高版本和 OS X v10.10 及更高版本中,在完成块开始执行后此属性设置为 nil。

另请参阅皮特·斯坦伯格的这条推文