Swift闭包是否保留捕获的变量?

时间:2014-10-26 15:20:25

标签: swift

我发现Swift闭包并不像我期望的那样保留捕获的变量。

class AAA {
}
var a1  =   AAA() as AAA?                  // expects RC == 1
var a2  =   { ()->AAA? in return a1 }      // expects RC == 2, retained by `Optional<AAA>`
a1      =   nil                            // expects RC == 1
a2()                                       // prints nil, ????

我对此非常困惑,因为我一直认为默认情况下会保留捕获的变量。但是,如果我使用捕获列表明确捕获它,它就会被保留。

class AAA {
}
var a1  =   AAA() as AAA?
var a2  =   { [a1]()->AAA? in return a1 }
a1      =   nil
a2() // prints {AAA}, alive as expected.

我重新阅读了Swift手册,但我找不到相关说明。捕获列表用于明确设置unowned,我仍然感到困惑。 什么是正确的行为以及为什么会发生这种情况?

3 个答案:

答案 0 :(得分:5)

是的,记录在Capturing Values

  

Swift确定应该通过引用捕获什么以及应该通过值复制什么。您不需要注释amount或runningTotal来表示它们可以在嵌套的incrementor函数中使用。 Swift还处理在incrementor函数不再需要时处理runningTotal所涉及的所有内存管理。

规则是:如果引用捕获的变量而不修改它,则按值捕获。如果您修改它,则通过引用捕获它。当然,除非你通过定义一个捕获列表明确地覆盖它。

附录以上陈述似乎不正确。无论是否在封闭内部进行修改,都可以通过引用进行捕获。阅读@newacct评论。

答案 1 :(得分:2)

你的例子没有意义@newacct。变量x实际上是在块外修改的。 Swift很聪明,可以找出你是否修改了闭包内部或外部的变量。

正如文件所说:

  

作为优化,如果该值未在闭包之外或之外变异,则Swift可以捕获并存储值的副本。

关于@Eonil的问题帖子,在第一个片段中,你在关闭时强烈引用了Optional<AAA>。但是,当你将a1设置为nil时,你实际做的是删除包含在可选项中的AAA类型的值。

当你调用closure a2时,你返回相同的可选项,里面没有值,这正是nil的意思。因此,持有强引用意味着您的闭包共享与a1相同的可选值。这并不意味着此选项不能设置为nil。

在第二个代码段中,您捕获了捕获列表中的a1。这意味着您复制a1,并且闭包内的值a1与闭包外的值a1无关。因此,即使将a1设置为nil,也不会影响从闭包中获得的内容。

我的英语不好,我希望我能清楚地表达我的意见。或者您可以阅读this article,我相信它会对您有所帮助。

答案 2 :(得分:1)

我在Apple开发者论坛上发布了同样的问题,并且有a discussion。虽然人们不是在谈论引用计数很多,但我有一些想法。这是我的结论:

  • 当值绑定到新(显式)名称时,始终会复制该值。无论var还是let
  • 参考类型中的
  • 复制表示RC + 1。因为它复制了强大的指针,如C ++ shared_ptr<T>
  • Closing(闭包捕获)不会改变任何东西,因为没有新名称。它与您在同一堆栈中使用它们相同。这就是 by-reference 的含义。与RC无关,也不会改变RC,因为它类似于C ++中的引用。
  • 捕获列表是一个显式(let)名称。所以它导致复制,它使RC + 1。
  • 当从函数返回闭包时,它可能绑定到名称。如果确实如此,RC + 1是因为新的绑定名称。
  • RC-1当一个值(如此强指针)从其名称中解除绑定时。
  • self的隐式引用是唯一允许隐式名称绑定的例外。

在没有违反这些规则的情况下进行了许多优化,无论如何,这只是实施细节。