我正在将我的 Swift 应用程序转换为使用组合以及
async/await
,并且我试图了解处理异步函数和主线程之间交互的最佳方法是什么。
这是一个加载用户的异步函数:
class AccountManager {
static func fetchOrLoadUser() async throws -> AppUser {
if let user = AppUser.current.value {
return user
}
let syncUser = try await loadUser()
let user = try AppUser(syncUser: syncUser)
AppUser.current.value = user // [warning]: "Publishing changes from background threads is not allowed"
return user
}
}
还有一堂课:
class AppUser {
static var current = CurrentValueSubject<AppUser?,Never>(nil)
// ...
}
注意:我选择使用
CurrentValueSubject
,因为它允许我 (1) 在需要时同步读取该值,以及 (2) 订阅更改。
现在,在上面标记的行上我收到错误
Publishing changes from background threads is not allowed
,我明白了。我看到了解决这个问题的不同方法:
AccountManager
类标记为 @MainActor
由于异步函数中完成的大部分工作都是等待网络结果,我想知道简单地在主线程上运行所有内容是否存在问题。这是否会导致性能问题?
DispatchQueue.main.sync
这是一个合理的解决方案,还是会导致死锁等线程问题?
DispatchGroup
与 enter()
、leave()
和 wait()
就像这个答案。与解决方案#2 有什么区别吗?因为这个解决方案需要更多的代码行,所以如果可能的话我宁愿不使用它——我更喜欢干净的代码。
您可以将呼叫包装在
await MainActor.run { }
块中。我认为这是最快捷的方法。
在使用 Swift Concurrency 时不应该使用 Dispatch 机制,尽管我认为
DispatchQueue.main.async { }
在这里可以安全使用。
@MainActor
属性是安全的,但不应该在 ObservableObject
上使用,并且如果 CPU 绑定代码在带注释类型的方法中运行,则可能会降低 UI 速度。