从 SwiftUI 视图中访问参与者的独立状态



我正在尝试理解从SwiftUI视图中访问actor类型中持有的隔离状态的设计模式。

以简单的代码为例:

actor Model: ObservableObject {
@Published var num: Int = 0

func updateNumber(_ newNum: Int) {
self.num = newNum
}
}
struct ContentView: View {
@StateObject var model = Model()

var body: some View {
Text("(model.num)") // <-- Compiler error: Actor-isolated property 'num' can not be referenced from the main actor
Button("Update number") {
Task.detached() {
await model.updateNumber(1)
}
}
}
}

可以理解的是,当我尝试访问隔离的值时,我得到编译器错误Actor-isolated property 'num' can not be referenced from the main actor。然而,我不明白如何在视图中显示这些数据。我想知道我是否需要一个观察演员的ViewModel,并在主线程上更新自己,但得到编译时错误Actor-isolated property '$num' can not be referenced from a non-isolated context

class ViewModel: ObservableObject {

let model: Model
@Published var num: Int
let cancellable: AnyCancellable

init() {
let model = Model()
self.model = model
self.num = 0
self.cancellable = model.$num
.receive(on: DispatchQueue.main)
.sink { self.num = $0 }
}

}

其次,想象一下,如果这段代码编译,那么我会得到另一个错误,当点击按钮,接口没有更新在主线程上…我不确定如何从演员的内部影响这一点?

你需要在你的视图模型(命名为Model)上使用MainActor,表明它中的所有调度都在主线程上(当然假设你将使用新的swift并发系统)。

所以它看起来像

@MainActor 
class Model: ObservableObject {
@Published var num: Int = 0
func updateNumber(_ newNum: Int) {
self.num = newNum
}
}

注意:我建议在类中进行所有更新,因为有人在某些地方可能会忘记以正确的方式更新

接受的答案可能不适合所有用例。

在我的情况下,我希望ObservableObject保持actor,但从SwiftUI视图访问其属性之一。因此,我只将@MainActor添加到该属性。

这意味着只需要从主线程更新属性,如下所示。

actor Model: ObservableObject {
@MainActor @Published var num: Int = 0
func updateNumber(_ newNum: Int) {
Task { @MainActor in 
self.num = newNum
}
}
// all other properties and functions will remain isolated to the actor
}

最新更新