来自Transitioning to ARC Release Notes
使用终身限定符来避免强参考周期
您可以使用生命周期限定符来避免强引用周期。对于 例如,通常如果你有一个排列在一个对象的图形 亲子等级和父母需要引用他们的孩子和 反之亦然,那么你就使父母与子女的关系变得强大 孩子与父母的关系薄弱。其他情况可能更多 微妙的,特别是当它们涉及块状物体时。
在手动引用计数模式下,
__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将不再被再次使用,即块何时到期。
答案 0 :(得分: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
};
当我们在块的末尾将object
分配给nil
时,该块将不再保留object
并且保留周期被破坏。这允许释放两个对象。
一个具体的例子是使用NSOperation
的{{1}}属性。如果使用completionBlock
来访问操作的结果,则需要中断创建的保留周期:
completionBlock
正如文档所述,您还可以使用许多其他技术来打破这些保留周期。例如,您需要在非ARC代码中使用与ARC代码中不同的技术。
答案 1 :(得分:0)
我更喜欢这个解决方案
typeof(self) __weak weakSelf = self;
self.rotationBlock = ^{
typeof (weakSelf) __strong self = weakSelf;
[self yourCodeThatReferenceSelf];
};
该块会将自身捕获为弱引用,并且不会有保留周期。然后在代码运行之前将块内的self重新定义为__strong self = weakSelf。这可以防止在块运行时释放self。