提问人:Michał Ziobro 提问时间:2/11/2022 最后编辑:Michał Ziobro 更新时间:8/10/2022 访问量:6649
Swift async/await 是什么 DispatchQueue.main.async 的替代品
Swift async/await what it the replacement of DispatchQueue.main.async
问:
在新的 Swift 5.5 中使用 async/await 并发机制时如何返回主线程?我应该只用@MainActor标记函数、类吗?
我还能使用吗?这是正确的吗?由于新机制不使用 GCD,并且异步任务和线程之间没有像以前那样的映射?DispatchQueue.main.async
例如,我将 SwiftUI 与List
refreshable
List { }
.refreshable {
viewModel.fetchData()
}
这样可以吗
List { }
.refreshable {
DispatchQueue.main.async {
viewModel.fetchData()
}
}
或者我需要在 ViewModel 类上添加@MainActor? 我没有在项目中使用 async/await,所以仅将 MainActor 用于这个单一的刷新对象似乎是多余的,我也不知道添加这样的属性如何影响 ViewModel 类的其余方法和属性,他们现在使用 Combine。
但另一方面,Xcode 显示
运行时:SwiftUI:不从后台线程发布更改 允许;确保从主线程发布值(通过 像 receive(on:)) 这样的运算符在模型更新时。
此外,在将@MainActor添加到 ViewModel 后,我收到多个这样的警告
与全局参与者“MainActor”隔离的属性“title”无法满足 协议的相应要求 “OnlineBankingListViewModelProtocol”(网上银行ListViewModelProtocol)
答:
在很多情况下,新的异步模型会计算出哪个线程最适合执行任务,如果你的任务可以访问 Guide 对象,编译器/运行时会选择在主线程中运行整个任务,如果你有其他东西想在主线程中运行, 你可以使用 @ MainActor,但是你在主线程中强制的东西越多,你在该线程上投入的工作就越多,而分配给其他线程的工作就越少,因此你的工作分散在多个内核上的机会就越少。如果你真的推动它,你可以得到一个任务在后台线程中运行,访问指南的东西,你会发现执行将跳转到主线程来执行 GUI 调用。如果你想在主线程中运行一组调用,并使用一些你编码的 gui 代码,只需将它们包装在一个任务中,运行时系统就会为你排序。
你问:
我还能使用吗?
DispatchQueue.main.async
如果你在一个方法中,并且想要将一些东西分派到主队列,那么最字面上的等价物是:async
MainActor.run { ... }
但更谨慎的做法是简单地用 标记方法(或其类)。这不仅可以确保它在主线程上运行它,而且如果您尝试从错误的参与者调用它,您会收到编译时警告。@MainActor
因此,如果您的视图模型标有 ,则无需手动运行任务。在处理被观察对象的已发布属性时尤其如此。@MainActor
MainActor
例如,请考虑:
@MainActor
class ViewModel: ObservableObject {
@Published var values: [Int] = []
func fetchData() async {
let foo = await ...
values = foo.values
}
}
然后
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
List {
...
}
.refreshable {
await viewModel.fetchData()
}
}
}
(请注意,我创建了一个方法,并在其中进行了调整,以便微调器准确反映进程何时运行。fetchData
async
await
refreshable
async
观看 WWDC 2021 视频 Swift 并发:更新示例应用。诚然,这说明了 UIKit 应用程序的过渡,但包括 和 的示例。@MainActor
MainActor.run
请注意,虽然 在很大程度上消除了 的需要,但在某些情况下,您可能会使用此模式。具体来说,如果你在某个其他 actor 上,并且想要在主线程上连续运行三个单独的函数,你可以将它们的系列包装在一个块中,从而通过对主 actor 的单个调度来运行所有三个函数,而不是三个单独的调用。@MainActor
MainActor.run { … }
run
@MainActor
MainActor.run { … }
上面,我重点介绍了突出的部分,但这是我的完整 MCVE:
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
List {
ForEach(viewModel.values, id: \.self) { value in
Text("\(value)")
}
}
.refreshable {
await viewModel.fetchData()
}
}
}
struct Foo: Decodable{
let json: [Int]
}
@MainActor
class ViewModel: ObservableObject {
@Published var values: [Int] = []
func fetchData() async {
do {
let foo = try await object(Foo.self, for: request)
values = foo.json
} catch {
print(error)
}
}
func object<T: Decodable>(_ type: T.Type, for request: URLRequest) async throws -> T {
let (data, response) = try await URLSession.shared.data(for: request)
guard let response = response as? HTTPURLResponse else {
throw URLError(.badServerResponse)
}
guard 200 ... 299 ~= response.statusCode else {
throw ApiError.failure(response.statusCode, data)
}
return try JSONDecoder().decode(T.self, from: data)
}
var request: URLRequest = {
let url = URL(string: "https://httpbin.org/anything")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "[1,2,3,4,5]".data(using: .utf8)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
return request
}()
}
enum ApiError: Error {
case failure(Int, Data)
}
评论
替代的是:DispatchQueue.main.async { foo.bar() }
Task { @MainActor in
print(Thread.current.isMainThread) // "true"
}
评论