访问 actor 的属性的问题在于,如果访问是在 actor 之外进行的field
,它需要并调用。await
这意味着您的 SwiftUI 代码中有一个暂停点,这意味着当 SwiftUI 代码恢复时,它可能不再在主线程上执行,这是一个大问题。
如果演员不做后台工作,那么 Asperi 使用 @MainAction 的解决方案会很好地工作,因为在这种情况下,SwiftUI 访问发生在主线程上。
但是如果actor在后台运行,您需要另一个同步点,它在主线程上运行代码,包装Foo
actor,并由您的视图使用:
actor Foo {
private(set) var field: [Bar]
func updateField(_ field: [Bar]) {
self.field = field
}
struct Bar {
// ...
}
}
class FooModel: ObservableObject {
private let foo: Foo
@Published var field: [Foo.Bar] = [] {
didSet {
Task { await foo.updateField(field) }
}
}
init(foo: Foo) {
self.foo = foo
Task { self.field = await foo.field }
}
}
struct MyView: View {
@StateObject
var foo: FooModel
然而,这只是故事的一半,因为您还需要在值更改时Foo
发送通知。您可以为此使用 a:FooModel
field
PassthroughSubject
actor Foo {
var field: [Bar] {
didSet { fieldSubject.send(field) }
}
private let fieldSubject: PassthroughSubject<[Bar], Never>
let fieldPublisher: AnyPublisher<[Bar], Never>
init() {
field = ... // initial value
fieldSubject = PassthroughSubject()
fieldPublisher = fieldSubject.eraseToAnyPublisher()
}
func updateField(_ field: [Bar]) {
self.field = field
}
struct Bar {
// ...
}
}
并订阅模型发布的内容:
class FooModel: ObservableObject {
private let foo: Foo
@Published var field: [Foo.Bar] = [] {
didSet {
Task { await foo.updateField(field) }
}
}
init(foo: Foo) {
self.foo = foo
Task { self.field = await foo.field }
foo.fieldPublisher.receive(on: DispatchQueue.main).assign(to: &$field)
}
}
如您所见,由于actor在任意线程上运行,而您的SwiftUI代码(或任何一般的UI代码)必须仅在主线程上运行,因此需要编写大量代码。