当我已经使用`.receive(on: .main)`时,为什么还需要使用`@MainActor`?

问题描述 投票:0回答:1

我不断收到运行时错误:

不允许从后台线程发布更改;确保在模型更新时从主线程发布值(通过像 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

ios swift swiftui
1个回答
0
投票

当您编写

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

© www.soinside.com 2019 - 2024. All rights reserved.