从SwiftUI视图中访问参与者的隔离状态

mwngjboj  于 2023-02-03  发布在  Swift
关注(0)|答案(2)|浏览(146)

我试图理解一种设计模式,用于从SwiftUI视图中访问参与者类型中保存的隔离状态。
看看下面这个简单的代码:

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,并在主线程上更新自己,但得到编译时错误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 }
    }
    
}

其次,想象一下,如果这段代码确实编译了,那么当我单击主线程上的接口没有更新的按钮时,我会得到另一个错误......我再次不确定如何从actor内部实现这一点?

ki1q1bka

ki1q1bka1#

您需要在视图模型(名为Model)上使用MainActor,以指示其中的所有调度都在主线程上(当然,假设您将为它使用新的swift并发系统)。
所以看起来

@MainActor 
class Model: ObservableObject {
    @Published var num: Int = 0

    func updateNumber(_ newNum: Int) {
        self.num = newNum
    }
}
  • 注意:我建议在类中进行所有更新,因为在某些地方的人可能会忘记以正确的方式更新它。*
igetnqfo

igetnqfo2#

可接受的答案可能不适合所有用例。
在我的例子中,我希望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
}

相关问题