且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Swift:闭包是否引用了常量或变量?

更新时间:2023-11-29 10:30:28

我认为造成混淆的原因是对值类型与引用类型的思考过于刻板。这与此无关。让我们以数字作为引用类型:

I think the confusion is by thinking too hard about value types vs reference types. This has very little to do with that. Let's make number be reference types:

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

这感觉有什么不同吗?我希望不是。是相同的,只是参考。这不是值/参考值。

Does this feel different in any way? I hope not. It's the same thing, just in references. This isn't a value/reference thing.

关键是,正如您所注意到的,闭包捕获变量。不是变量的值,也不是变量指向的引用的值,而是变量本身。因此,在捕获该变量的所有其他位置(包括调用方),都可以看到对闭包内部变量的更改。在捕获值

The point is that, as you note, the closure captures the variable. Not the value of the variable, or the value of the reference the variable points to, but the variable itself). So changes to the variable inside the closure are seen in all other places that have captured that variable (including the caller). This is discussed a bit more fully in Capturing Values.

如果您有兴趣的话,请加深了解(现在我要讲一点技巧)

A bit deeper if you're interested (now I'm getting into a bit of technicalities that may be beyond what you care about right now):

闭包实际上是对该变量的引用,并且它们会立即发生更改,包括调用 didSet 等。这与 inout 参数不同,后者仅在返回时将值分配给其原始上下文。您可以看到这种方式:

Closures actually have a reference to the variable, and changes they make immediately occur, including calling didSet, etc. This is not the same as inout parameters, which assign the value to their original context only when they return. You can see that this way:

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

请注意,设置计数始终位于增加计数,但在增加计数之后。这使人们知道闭包实际上是指它们捕获的同一个变量(而不是值或引用;变量),以及为什么我们称其为闭包捕获,而不是传递给函数。 (当然,您也可以传递到闭包,在这种情况下,它们的行为与这些参数上的函数完全一样。)

Note how "set count" is always before "incremented count" but is after "increased count." This drives home that closures really are referring to the same variable (not value or reference; variable) that they captured, and why we call it "capturing" for closures, as opposed to "passing" to functions. (You can also "pass" to closures of course, in which case they behave exactly like functions on those parameters.)