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

Phi*_*den 7 concurrency swift swiftui

我试图理解一种设计模式,用于从 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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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 }
    }
    
}
Run Code Online (Sandbox Code Playgroud)

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

Asp*_*eri 5

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

所以它看起来像

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

    func updateNumber(_ newNum: Int) {
        self.num = newNum
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:我建议在课堂上进行所有更新,因为某个地方的某人可能会忘记以正确的方式更新它。


Imt*_*ath 5

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

就我而言,我希望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
}
Run Code Online (Sandbox Code Playgroud)