为什么我们必须将__block变量设置为nil? [英] Why do we have to set __block variable to nil?

查看:239
本文介绍了为什么我们必须将__block变量设置为nil?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用生命周期限定词来避免强大的参考周期

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.

在手动参考计数模式下,__block id x;具有不 保留x.在ARC模式下,__block id x;默认保留为x(仅 像所有其他值一样).获取手动参考计数模式 行为在ARC下,您可以使用__unsafe_unretained __block id x;. 正如名称__unsafe_unretained所暗示的那样, 非保留变量很危险(因为它可能悬挂),并且是 因此灰心丧气.两种更好的选择是使用__weak(如果 您不需要支持iOS 4或OS X v10.6),也可以设置__blocknil打破保留周期.

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变量有何不同?

为什么在这里设置为nil? __block变量是否保留两次?谁拥有所有参考资料?块?堆吗堆栈?线程?什么?

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?

以下代码片段使用有时在手动引用计数中使用的模式来说明此问题.

The following code fragment illustrates this issue using a pattern that is sometimes used in manual reference counting.

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:

As described, instead, you can use a __block qualifier and set the myController variable to nil in the completion handler:

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,即该块何时终止.

Also why myController is not set to nil by compiler. Why do we have to do so? It seems that the compiler sort of know when myController will no longer be used again namely when the block expire.

推荐答案

当您具有以下形式的代码时:

When you have code of this form:

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

object将保留或复制您提供给它的块.但是该块本身也将保留object,因为它是从该块内部强烈引用的.这是一个保留周期.即使在块完成执行之后,参考循环仍然存在,并且对象和块都无法释放.请记住,一个块可以被多次调用,所以它不能只忘记执行完一次就忘记它引用的所有变量.

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.

要打破此循环,可以将object定义为__block变量,该变量允许您从块内部更改其值,例如将其更改为nil即可中断循环:

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

当我们在块的末尾将object分配给nil时,该块将不再保留object,并且保留循环中断.这样可以释放两个对象.

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.

对此的一个具体示例是使用NSOperationcompletionBlock属性.如果使用completionBlock访问操作的结果,则需要中断创建的保留周期:

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

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

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.

这篇关于为什么我们必须将__block变量设置为nil?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆