我认为混乱是由于太过努力的价值类型与参考类型。这与此无关。让我们将数字作为参考类型:
class RefInt: CustomStringConvertible {
let value: Int
init(value: Int) { self.value = value }
var description: String { return "\(value)" }
}
let counter:() -> RefInt
var count = RefInt(value: 0)
do {
counter = {
count = RefInt(value: count.value + 1)
return count
}
}
count = RefInt(value: count.value + 1) // 1
counter() // 2
counter() // 3
这种感觉有什么不同吗?我希望不是。这是同样的事情,只是在参考。这不是一个价值/参考的东西。
问题是,正如你注意到的那样,闭包捕获变量。不是变量的值,或变量指向的引用的值,而是变量本身)。因此,在所有其他捕获该变量的位置(包括调用者)中都会看到对闭包内变量的更改。在Capturing Values中对此进行了更全面的讨论。如果你有兴趣(我现在进入有点技术性,可能会超出你在乎什么,现在的)
深一点:
瓶盖居然有变量的引用,并改变它们会立即发生,包括调用didSet
等。这与inout
参数不同,该参数仅在返回时才将值分配给其原始上下文。你可以看到,这种方式:
let counter:() -> Int
var count = 0 {
didSet { print("set count") }
}
do {
counter = {
count += 1
print("incremented count")
return count
}
}
func increaseCount(count: inout Int) {
count += 1
print("increased Count")
}
print("1")
count += 1 // 1
print("2")
counter() // 2
print("3")
counter() // 3
increaseCount(count: &count)
这版画:“增加数量”
1
set count
2
set count
incremented count
3
set count
incremented count
increased Count
set count
注“设定计数”怎么总是前“增加的计”,但后这使得闭包真正引用了它们捕获的同一个变量(而不是值或引用;变量),以及为什么我们称它为捕获闭包,而不是“传递”到函数。 (当然,您也可以“通过”关闭,在这种情况下,它们的行为与这些参数的功能完全相同。)
我不确定你想获得多少技术细节,但https://stackoverflow.com/a/40979548/2976878&https://stackoverflow.com/q/43171341/2976878可能是有用。基本上,捕获的值被放入堆中的引用计数框中,然后存储在函数值的上下文对象中。当涉及到捕获具有引用类型的变量时,引用本身被放入堆分配框(考虑引用*本身*是值类型),所以您实际上是对引用的引用。 – Hamish
我看过你的文章,这很刺激。我了解内容。但是,什么是参考计数盒?我读了你多次展示的答案,但我没有见过这个词。你能解释一下吗? –
很高兴你发现它很有用!一个盒子只是一个给定值的包装,所以在闭包捕获时,捕获的变量的值在结构中堆积为“盒子”,并在我的答案中包含专门的“盒子'结构的布局。然后(主要)对该变量的任何未来访问(在其定义的范围内以及闭包中)仅被编译为访问堆上的装箱值。 –
Hamish