如何在此块中强烈修复“捕获'块'可能会导致保留周期”

时间:2013-03-26 13:50:26

标签: ios objective-c asynchronous objective-c-blocks retain-cycle

我正在研究这个代码,它在网上执行一些冗长的异步操作,当它完成时会触发一个完成块,执行一些测试,如果一个变量得到一个值,另一个冗长的操作应该立即开始:

-(void) performOperation
{

    void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request){


        int variable=0;

        // Do completion operation A
        //...
        //...

        // Do completion operation B                
        //Get the variable value

        if(variable>0){
            [self doLengthyAsynchronousOperationWithCompletionBlock: completionBlock];
        }

    };

//Perform the lenhgty operation with the above completionBlock
    [self doLengthyAsynchronousOperationWithCompletionBlock: completionBlock];

}

-(void) doLengthyAsynchronousOperationWithCompletionBlock: completionBlock
{
    //Do some lengthy asynchronous stuff
}

使用此代码,我会从编译器收到此警告:

WARNING: Block pointer variable 'completionBlock' is uninitialized when caputerd by the block

我改变了:

void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request)

在:

__block void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request)

但是我得到了另一个警告:

WARNING 2: Capturing 'completionBlock' strongly in this block is likely to lead to a retain cycle

我该如何解决这个问题?

由于

尼古拉

1 个答案:

答案 0 :(得分:29)

  

警告:块指针变量'completionBlock'未初始化   当被块捕获时

这是因为初始化为递归块的块变量需要__block存储。

  • 除非使用__block声明,否则将复制块中的变量,在这种情况下,它们将作为参考传递。
  • 当为块变量分配递归块时,创建在赋值之前发生,并且此类创建会触发变量副本。如果尚未分配变量,则复制​​的变量将是一个错误值,并且在运行块时将产生崩溃。
  • 但是如果我们添加__block,则会创建一个带有对变量的引用的块。然后变量将被初始化为创建的块,块就可以使用了。
  

警告:可能会在此块中强烈捕获'completionBlock'   导致保留周期

这是因为块变量是对块的强引用,并且块本身引用变量(因为正如我们之前看到的,变量具有__block,因此它被引用而不是复制)。 / p>

所以我们需要

  • 对块内强变量的弱引用。
  • 并且在外部有一个强引用,以防止在创建它的方法范围内释放块。
    void(^ completionBlock) (id obj, NSError *err, NSURLRequest *request);
    void(^ __block __weak weakCompletionBlock) (id obj, NSError *err, NSURLRequest *request);
    weakCompletionBlock = completionBlock = ^(id obj,NSError *err, NSURLRequest *request){
        [self lengthyAsyncMethod:weakCompletionBlock];
    };

名称doLengthyAsynchronousOperationWithCompletionBlock表明该方法可能比创建块的方法范围更长。鉴于编译器不复制作为参数传递的块,此方法的责任是复制此块。如果我们将此块与块感知代码一起使用(例如:dispatch_async()),则会自动发生这种情况。

如果我们一直将这个块分配给一个实例变量,我们需要一个@property(copy)和一个对块内的self的弱引用,但事实并非如此,所以我们只使用self。