2012-06-12 140 views
6

Transitioning to ARC Release Notes为什么我们必须将__block变量设置为零?

使用生命周期预选赛避免强烈参考周期

您可以使用一辈子预选赛避免强烈的参考周期。例如,对于 示例,通常如果您有一个排列在 亲子分层结构中的对象图,并且父母需要引用其子女,并且反之亦然,则可以使父母对子女关系更强壮,并且子对象关系为 - 亲密关系弱。其他情况可能更为微妙,特别是涉及块对象时。

在手动参考计数模式下,__block id x;的效果不是 保留x。在ARC模式下,__block id x;默认为保留x(与所有其他值一样,仅为 )。要获得ARC下的手动引用计数模式 行为,可以使用__unsafe_unretained __block id x;。 因为名称__unsafe_unretained意味着,但是,具有 非保留变量是危险的(因为它可能会晃动),因此不鼓励 。两个更好的选择是使用__weak(如果 您不需要支持iOS 4或OS X v10.6),或者将__block 的值设置为nil以打破保留周期。

好的,那么__block变量有什么不同?

为什么在这里设置为nil?是__block变量保留两次?谁拥有所有的参考?该块?堆?堆栈?线程?什么?

以下代码段使用有时在手动引用计数中使用的模式说明了此问题。

MyViewController *myController = [[MyViewController alloc] init…]; 

// ... 

myController.completionHandler = ^(NSInteger result) { 
    [myController dismissViewControllerAnimated:YES completion:nil]; 
}; 

[self presentViewController:myController animated:YES completion:^{ 
    [myController release]; 
}]; 

如上所述,相反,你可以使用一个__block预选赛,并在完成处理的变量myController的设置为nil

MyViewController * __block myController = [[MyViewController alloc] init…]; //Why use __block. my controller is not changed at all 

// ... 

myController.completionHandler = ^(NSInteger result) { 
    [myController dismissViewControllerAnimated:YES completion:nil]; 

    myController = nil; //Why set to nil here? Is __block variable retained twice? Who hold all the reference? The block? The heap? The stack? The thread? The what? 
}; 

而且为什么myController不是由编译器设置为nil。为什么我们必须这样做?似乎编译器知道什么时候myController将不再使用,即块何时到期。

回答

14

当你有这种形式的代码:

object.block = ^{ 
    // reference object from inside the block 
    [object someMethodOrProperty]; 
}; 

object将保留或复制你给它的块。但块本身也将保留object,因为它在块内被强引用。这是一个保留周期。即使在块完成执行后,参考周期仍然存在,并且对象和块都不能被解除分配。记住一个块可以被多次调用,所以它不能仅仅在它完成一次执行后忘记它所引用的所有变量。

要打破此循环,您可以将object定义为__block变量,该变量允许您从块内更改其值,例如,将其更改为nil打破循环:

__block id object = ...; 
object.block = ^{ 
    // reference object from inside the block 
    [object someMethodOrProperty]; 

    object = nil; 
    // At this point, the block no longer retains object, so the cycle is broken 
}; 

当我们在块的末尾分配objectnil,块将不再保留object和保留周期被打破。这允许两个对象被释放。

这个的一个具体例子是用NSOperationcompletionBlock属性。如果使用completionBlock访问操作的结果,你需要打破保留所创建的周期:

__block NSOperation *op = [self operationForProcessingSomeData]; 
op.completionBlock = ^{ 
    // since we strongly reference op here, a retain cycle is created 
    [self operationFinishedWithData:op.processedData]; 

    // break the retain cycle! 
    op = nil; 
} 

由于文档描述,还有一些其他的技巧,你也可以用它来打破这些保留周期。例如,您需要在非ARC代码中使用与ARC代码中不同的技术。

+0

“但块本身也会保留对象,因为它在块内被强引用。”为什么?关闭。 –

+0

添加__block如何产生任何影响? –

+0

当一个块捕获一个指向一个objective-c对象的指针时,除非使用'__weak'或'__unsafe_unretained'(或者非ARC代码中的'__block'),否则该对象将被保留。 –

0

我喜欢这个解决方案

typeof(self) __weak weakSelf = self; 
self.rotationBlock = ^{ 
    typeof (weakSelf) __strong self = weakSelf; 

    [self yourCodeThatReferenceSelf]; 
}; 

会发生什么情况是,该块将捕获自我为参考,并不会有任何保留周期。在你的代码运行之前,块内的自我被重新定义为__strong self = weakSelf。这可以防止在您的街区运行时自我释放。

相关问题