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_unretained
implies,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 tonil
to 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; }