为什么捕获 onChange(of:perform:) 的参数?

Why is parameter for onChange(of:perform:) captured?

我使用的是 onChange(of:perform:) SwiftUI 修饰符。然后我想获得旧值,将其与新值进行比较。我阅读了文档,上面写着:

The previous value may be captured by the closure to compare it to the new value.

举个例子:

.onChange(of: playState) { [playState] newState in
    model.playStateDidChange(from: playState, to: newState)
}

我的问题是为什么示例中的playState是在[ ]中捕获的? playState 值无需传入即可轻松访问。此外,这不是 class 的一部分,因此我认为无法通过捕获 [=15= 来创建强引用] 某种形式。

为什么例子要这样写?

在“正常”情况下,在不可变 value-type(例如 struct)中定义的闭包捕获其值,该值不会改变,所以一切都很好。

struct Foo {
  var a = "original"
  
  func makeFn() -> () -> Void {
     return { print(a) }
  }
}

var foo = Foo()
let fn = foo.makeFn()
foo.a = "changed"

fn() // "original"

但是对于 @State,实际值存储在 SwiftUI 维护的一些全局存储中,因此它基本上表现得好像它具有引用语义。

当调用闭包时,状态值已经改变,所以像上面那样做 print(a) 通过 @State 属性 包装器访问一个值,它检索 then-updated值。

为了解决这个问题,您可以使用捕获列表将 属性 捕获到闭包的局部变量中:

return { [a] in print(a) }

这当然是一个简化的示例,SwiftUI 可能在幕后做其他事情,但我认为它传达了这一点。


要查看 SwiftUI 中的差异,请尝试以下操作:

.onChange(of: playState) { [playState] newState in
    print(playState, self.playState, newState)
}

输出将是这样的:

original new new

playState 是定义闭包时捕获的局部变量(即计算 body 时),self.playState 通过 @State 访问该值,这已经改变了,newState显然是具有新值的passed-in参数。