我认为造成混乱的原因是对值类型与引用类型的思考过于深入。这与此关系不大。让我们将 number 设为引用类型:
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
这感觉有什么不同吗?我希望不是。道理是一样的,只是参考文献而已。这不是一个价值/参考的东西。
关键是,正如您所注意到的,闭包捕获了变量。不是变量的值,也不是变量指向的引用的值,而是变量本身)。因此,在捕获该变量(包括调用者)的所有其他地方都可以看到闭包内变量的更改。这在中进行了更全面的讨论捕捉价值观.
如果您感兴趣的话,可以更深入一些(现在我正在讨论一些可能超出您现在关心的技术细节):
闭包实际上有对变量的引用,并且它们所做的更改立即发生,包括调用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
请注意“设置计数”始终位于“增量计数”之前但位于“增量计数”之后。这让我们明白闭包实际上引用的是它们捕获的同一个变量(不是值或引用;变量),以及为什么我们称其为闭包的“捕获”,而不是“传递”到函数。 (当然,您也可以“传递”到闭包,在这种情况下,它们的行为与这些参数上的函数完全相同。)