为什么我们必须将__block变量设置为nil?

use*_*951 6 objective-c xcode4.3

过渡到ARC发行说明

使用终身限定符来避免强引用周期

您可以使用生命周期限定符来避免强引用周期.例如,通常如果您有一个以父子层次结构排列的对象图表,并且父母需要引用他们的孩子,反之亦然,那么您将使父母与子女之间的关系变得强大,并且父母与子女之间的关系变弱.其他情况可能更微妙,特别是当它们涉及块对象时.

在手动参考计数模式下,__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];
}];
Run Code Online (Sandbox Code Playgroud)

如上所述,您可以使用__block限定符并nil在完成处理程序中将myController变量设置为:

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?
};
Run Code Online (Sandbox Code Playgroud)

另外为什么myControllernil通过编译器设置.为什么我们必须这样做?似乎编译器知道何时mycontroller将不再被再次使用,即块何时到期.

Mik*_*ler 14

如果您有此表单的代码:

object.block = ^{
    // reference object from inside the block
    [object someMethodOrProperty];
};
Run Code Online (Sandbox Code Playgroud)

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
};
Run Code Online (Sandbox Code Playgroud)

当我们分配objectnil在该块结束时,该块将不再保留object和保留周期被打破.这允许两个对象被释放.

一个具体的例子是with 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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这实际上很常见.`NSOperation*operation = ...; operation.completionBlock = ^ {//在操作完成时执行的代码,通常会尝试访问操作}`.这只是一个例子. (2认同)