objective-c – 为什么我们必须将__block变量设置为nil?

前端之家收集整理的这篇文章主要介绍了objective-c – 为什么我们必须将__block变量设置为nil?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
Transitioning to ARC Release Notes

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
retaining x. In ARC mode,__block id x; defaults to retaining x (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 to nil 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;
}

正如文档所述,您还可以使用许多其他技术来打破这些保留周期.例如,您需要在非ARC代码中使用与ARC代码中不同的技术.

猜你在找的C&C++相关文章