Swift:闭包是否有对常量或变量的引用?

时间:2017-06-20 00:50:37

标签: swift closures

我知道有几个相关的问题,而且我可以在互联网上找到很多帖子。 但是,我无法理解闭包可以保存引用的事实。如果是引用类型,则通常非常合理,但值类型如何,包括structenum? 请参阅此代码。

let counter: () -> Int
var count = 0
do  {
    counter = {
        count += 1
        return count
    }
}
count += 1 // 1
counter() // 2
counter() // 3

我们可以通过两种方式访问​​值类型count。一种是直接使用count而另一种是通过闭包counter。 但是,如果我们写

let a = 0
let b = a

,在内存b当然有一个与a不同的区域,因为它们是值类型。此行为是值类型的一个显着特征,它与引用类型不同。 然后支持闭包主题,closure具有对value类型的变量或常量的引用。

那么,我可以说,在闭包捕获值的情况下,我们不能对值类型进行任何引用的值类型的特性会改变吗? 对我而言,捕获对值类型的引用是非常令人惊讶的,同时我在上面展示的经验表明了这一点。

你能解释一下吗?

1 个答案:

答案 0 :(得分:3)

我认为混淆是因为对价值类型与参考类型的思考过于严格。这与此无关。让数字为引用类型:

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

注意"设置计数"永远在"增加计数"但是在增加计数之后。"这使得闭包真正指的是他们捕获的相同变量(不是值或参考;变量),以及为什么我们称之为"捕获"对于关闭,而不是"传递"功能。 (当然,您也可以"传递"到闭包,在这种情况下,它们的行为与这些参数上的函数完全相同。)