6

过渡到 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];
}];

如前所述,您可以使用__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?
};

还有为什么编译器myController没有设置为nil。为什么我们必须这样做?似乎编译器知道什么时候 myController 将不再被使用,即块何时到期。

4

2 回答 2

14

When you have code of this form:

object.block = ^{
    // reference object from inside the block
    [object someMethodOrProperty];
};

object will retain or copy the block you give to it. But the block itself will also retain object because it is strongly referenced from within the block. This is a retain cycle. Even after the block has finished executing, the reference cycle still exists and neither the object nor the block can be deallocated. Remember that a block can be called multiple times, so it cannot just forget all the variables it references after it has finished executing once.

To break this cycle, you can define object to be a __block variable, which allows you to change its value from inside the block, e.g. changing it to nil to break the cycle:

__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
};

When we assign object to nil at the end of the block, the block will no longer retain object and the retain cycle is broken. This allows both objects to be deallocated.

One concrete example of this is with with NSOperation's completionBlock property. If you use the completionBlock to access an operation's result, you need to break the retain cycle that is created:

__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;
}

As the documentation describes, there are a number of other techniques you can also use to break these retain cycles. For example, you will need to use a different technique in non-ARC code than you would in ARC code.

于 2012-06-12T14:05:36.533 回答
0

我更喜欢这个解决方案

typeof(self) __weak weakSelf = self;
self.rotationBlock = ^{
    typeof (weakSelf) __strong self = weakSelf;

    [self yourCodeThatReferenceSelf];
};

发生的情况是该块将捕获 self 作为引用,并且不会有保留周期。然后在代码运行之前将块内的 self 重新定义为 __strong self = weakSelf。这可以防止 self 在您的块运行时被释放。

于 2014-11-11T13:36:26.597 回答