异步函数 Swift & Combine 的最佳实践



我正在将我的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.将DispatchGroupenter()leave()wait()一起使用

就像这个答案一样。解决方案#2有什么不同吗?因为这个解决方案需要更多的代码行,所以如果可能的话,我宁愿不使用它——我更喜欢干净的代码。

您可以将调用封装在await MainActor.run { }块中。我认为这是最快捷的方法。

在使用Swift并发时不应该使用Dispatch机制,尽管我认为DispatchQueue.main.async { }在这里使用是安全的。

@MainActor属性是安全的,但不应在ObservableObject上使用,并且如果在注释类型的方法中运行CPU绑定代码,则可能会降低UI的速度。

最新更新