我发现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
,我仍然感到困惑。
什么是正确的行为以及为什么会发生这种情况?
答案 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
。shared_ptr<T>
。let
)名称。所以它导致复制,它使RC + 1。self
的隐式引用是唯一允许隐式名称绑定的例外。在没有违反这些规则的情况下进行了许多优化,无论如何,这只是实施细节。