代码之家  ›  专栏  ›  技术社区  ›  nico9T

如何修复“在这个区块中强烈捕获‘区块’可能会导致保留周期”

  •  12
  • nico9T  · 技术社区  · 11 年前

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

    -(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 回复  |  直到 11 年前
        1
  •  29
  •   Jano teo    11 年前

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

    发生这种情况是因为初始化为递归块的块变量需要 __block 存储

    • 块中的变量将被复制,除非用声明 __块 ,在这种情况下,它们作为引用传递。
    • 当递归块被分配给块变量时,创建发生在分配之前,这样的创建会触发变量复制。假设变量尚未赋值,则复制的变量将是一个错误的值,并且在块运行时会产生崩溃。
    • 但如果我们加上 __块 ,则将使用对变量的引用来创建块。然后,变量将被初始化为创建的块,并且该块将准备好使用。

    警告:很可能在此块中强烈捕获“completionBlock” 导致一个保留周期

    发生这种情况是因为块变量是对块的强引用,而块本身正在引用该变量(因为正如我们之前所看到的,该变量具有 __块 因此它被引用而不是被复制)。

    所以我们需要

    • 对块内的强变量的弱引用。
    • 以及外部的强引用,以防止在创建块的方法的作用域期间释放该块。
        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。