12

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

-(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

我怎样才能解决这个问题?

谢谢

尼古拉

4

1 回答 1

29

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

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

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

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

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

所以我们需要

  • 对块内强变量的弱引用。
  • 以及外部的强引用,以防止在创建块的方法范围内释放块。
    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 longyAsyncMethod:weakCompletionBlock];
    };

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

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

于 2013-03-26T14:19:02.467 回答