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

人气:804 发布:2022-10-16 标签: swiftui swift automatic-ref-counting

问题描述

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

闭包可以捕获上一个值以将其与新值进行比较。

举例:

.onChange(of: playState) { [playState] newState in
    model.playStateDidChange(from: playState, to: newState)
}
我的问题是,为什么在该示例中playState[ ]中被捕获?playState值无需传入即可轻松访问。此外,这不是类的一部分,所以我假设没有办法通过捕获某种类型的self来创建强引用。

为什么该示例是这样编写的?

推荐答案

在正常情况下,在不可变值类型(例如,astruct)中定义的闭包捕获其值,该值不会更改,因此一切正常。

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属性包装器访问值,该包装器将检索随后更新的值。

为了应对这种情况,您可以使用捕获列表将该属性捕获到闭包的局部变量中:

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显然是传入的带有新值的参数。

854