为什么要捕获onChangeof:perform :)的参数?

问题描述

我正在使用onChange(of:perform:) SwiftUI修饰符。然后,我想获取旧值,将其与新值进行比较。我阅读了说明文件

闭包可能会捕获先前的值,以将其与新值进行比较。

举个例子:

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

我的问题是,为什么在示例中playState中捕获了[ ]?无需传递playState值就可以很容易地对其进行访问。此外,这也不是类的一部分,因此我认为无法通过捕获某种类型的self来创建强引用

为什么示例是这样写的?

解决方法

在“正常”情况下,在不可变值类型(例如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属性包装器访问一个值,该包装器检索随后更新的值。

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

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