Use Lifetime Qualifiers to Avoid Strong Reference Cycles
You can use lifetime qualifiers to avoid strong reference cycles. For
example,typically if you have a graph of objects arranged in a
parent-child hierarchy and parents need to refer to their children and
vice versa,then you make the parent-to-child relationship strong and
the child-to-parent relationship weak. Other situations may be more
subtle,particularly when they involve block objects.In manual reference counting mode,
__block id x;has the effect of not
retainingx. In ARC mode,__block id x;defaults to retainingx(just
like all other values). To get the manual reference counting mode
behavior under ARC,you Could use__unsafe_unretained __block id x;.
As the name__unsafe_unretainedimplies,however,having a
non-retained variable is dangerous (because it can dangle) and is
therefore discouraged. Two better options are to either use__weak(if
you don’t need to support iOS 4 or OS X v10.6),or set the__block
value tonilto break the retain cycle.
好的,那么__block变量有什么不同?
为什么在这里设置为零? __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将不再被再次使用,即块何时到期.
解决方法
object.block = ^{
// reference object from inside the block
[object someMethodorProperty];
};
对象将保留或复制您提供给它的块.但是块本身也将保留对象,因为它在块内被强烈引用.这是一个保留周期.即使在块完成执行之后,参考循环仍然存在,并且对象和块都不能被释放.请记住,可以多次调用一个块,因此它不能忘记它在执行一次后引用的所有变量.
要打破此循环,您可以将对象定义为__block变量,这允许您从块内部更改其值,例如将其更改为零以打破周期:
__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
};
当我们在块的末尾将对象分配给nil时,该块将不再保留对象并且保留周期被破坏.这允许两个对象被释放.
一个具体的例子是使用NSOperation的completionBlock属性.如果使用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代码中不同的技术.