我正在将我的Swift应用程序转换为使用Combine和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
,我理解了。我看到了解决这个问题的不同方法:
1.将整个AccountManager
类标记为@MainActor
由于异步函数中完成的大部分工作都是等待网络结果,我想知道在主线程上简单地运行所有内容是否存在问题。这是否会导致性能问题?
2.DispatchQueue.main.sync
中的Englobe错误行
这是一个合理的解决方案吗?还是会导致死锁之类的线程问题?
3.将DispatchGroup
与enter()
、leave()
和wait()
一起使用
就像这个答案一样。解决方案#2有什么不同吗?因为这个解决方案需要更多的代码行,所以如果可能的话,我宁愿不使用它——我更喜欢干净的代码。
您可以将调用封装在await MainActor.run { }
块中。我认为这是最快捷的方法。
在使用Swift并发时不应该使用Dispatch机制,尽管我认为DispatchQueue.main.async { }
在这里使用是安全的。
@MainActor
属性是安全的,但不应在ObservableObject
上使用,并且如果在注释类型的方法中运行CPU绑定代码,则可能会降低UI的速度。