我不断收到运行时错误:
不允许从后台线程发布更改;确保在模型更新时从主线程发布值(通过像 receive(on:) 这样的运算符)。
当我将
@Published
数组 = 设置为新值时,会发生这种情况。数组的设置发生在 .receive(on: DispatchQueue.main).sink
块内。
示例代码:
class MyClass: ObservableObject {
@Published var movies: [Movie] = []
func retrieve() async throws {
retrievePublisher = retrieveMovies().receive(on: DispatchQueue.main).sink(receiveCompletion: { _ in
}, receiveValue: { [weak self] newArray in
guard let self else { return }
movies = newArray
})
}
}
但是,如果我用
@MainActor
标记该类,则不再出现运行时错误。
此外,如果我在电影变量上或我设置它的位置设置断点,我可以观察到一切确实发生在主线程上。
为什么在这种情况下需要
@MainActor
? .receive(on: DispatchQueue.main)
导致了错误:Publishing changes from background threads is not allowed
?
当您编写
async
代码时,您可以删除合并的 ObservableObject
并使用 .task
修饰符,该修饰符在幕后创建一个引用类型,使异步工作与 UI 相关联,例如
@State var movies: [Movie] = []
...
.task { // runs when this UI appears and cancelled if it dissapears
movies = await retrieveMovies()
}
回到您的组合
ObservableObject
:使用 sink
是错误的,因为管道的生命周期与对象无关,在 ObservableObject
中完成管道的正确操作符是 assign
到@Published
。在 func 中设置管道也是一个错误,应该在 init
中设置管道,然后它可以自动处理传入的数据。最后,如果您不使用任何组合运算符(例如 combine
、merge
、zip
等),那么使用合并通常是错误的。话虽如此,您只需删除合并即可避免所有这些情况 ObservableObject
完全,当您希望在某些内容(即 .task
)发生变化时重新启动时,只需使用 .task(id:)
和 id
。