Mic*_*bro 14 async-await swift
在新的 Swift 5.5 中使用 async/await 并发机制时如何返回主线程?我应该只用@MainActor 标记函数、类吗?我还能用吗DispatchQueue.main.async?会是正确的吗?因为新机制不使用 GCD 并且异步任务和线程之间没有像以前那样的映射?
例如我正在使用ListSwiftUIrefreshable
List { }
.refreshable {
viewModel.fetchData()
}
Run Code Online (Sandbox Code Playgroud)
这个可以吗
List { }
.refreshable {
DispatchQueue.main.async {
viewModel.fetchData()
}
}
Run Code Online (Sandbox Code Playgroud)
或者我需要在 ViewModel 类上添加 @MainActor ?我在项目中没有使用 async/await,因此仅使用 MainActor 来实现这个单一的可刷新似乎是多余的,我也不知道添加此类属性如何影响 ViewModel 类的其余方法和属性,他们现在使用合并。
但另一方面 Xcode 显示
运行时:SwiftUI:不允许从后台线程发布更改;确保在模型更新时从主线程发布值(通过像 receive(on:) 这样的运算符)。
此外,在将 @MainActor 添加到 ViewModel 后,我收到多个这样的警告
与全局参与者“MainActor”隔离的属性“标题”无法满足协议“OnlineBankingListViewModelProtocol”的相应要求
Rob*_*Rob 14
你问:
\n\n\n我还能用吗
\nDispatchQueue.main.async?
如果您在一个async方法中并且想要将某些内容分派到主队列,那么最字面的等效内容是:
MainActor.run { ... }\nRun Code Online (Sandbox Code Playgroud)\n但更谨慎的做法是简单地用 来标记方法(或其类)@MainActor。这不仅可以确保它在主线程上运行它,而且如果您尝试从错误的参与者调用它,您还会收到编译时警告。
因此,如果您的视图模型标记为,则无需@MainActor在 上手动运行任务。MainActor在处理被观察对象的已发布属性时尤其如此。
例如,考虑:
\n@MainActor\nclass ViewModel: ObservableObject {\n @Published var values: [Int] = []\n\n func fetchData() async {\n let foo = await ...\n values = foo.values\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n进而
\nstruct ContentView: View {\n @ObservedObject var viewModel = ViewModel()\n\n var body: some View {\n List {\n ...\n }\n .refreshable {\n await viewModel.fetchData()\n }\n\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n(注意,我在其中创建了fetchData一个async方法,以便旋转器能够准确地反映进程何时运行。)awaitrefreshableasync
请参阅 WWDC 2021 视频Swift 并发:更新示例应用程序。这无疑说明了 UIKit 应用程序的转变,但包括@MainActor和的示例MainActor.run。
请注意,虽然@MainActor, 很大程度上消除了对 的需要MainActor.run { \xe2\x80\xa6 },但在某些情况下您仍然可以使用此run模式。具体来说,如果您在其他某个 actor 上并且想要@MainActor在主线程上连续运行三个单独的函数,您可以将它们的系列包装在一个单独的函数中MainActor.run { \xe2\x80\xa6 }块中,从而通过一次调度到主线程来运行所有三个函数演员,而不是三个单独的电话。
上面,我重点介绍了重要部分,但以下是我的完整 MCVE:
\nstruct ContentView: View {\n @ObservedObject var viewModel = ViewModel()\n\n var body: some View {\n List {\n ForEach(viewModel.values, id: \\.self) { value in\n Text("\\(value)")\n }\n }\n .refreshable {\n await viewModel.fetchData()\n }\n\n }\n}\n\nstruct Foo: Decodable{\n let json: [Int]\n}\n\n@MainActor\nclass ViewModel: ObservableObject {\n @Published var values: [Int] = []\n\n func fetchData() async {\n do {\n let foo = try await object(Foo.self, for: request)\n values = foo.json\n } catch {\n print(error)\n }\n }\n\n func object<T: Decodable>(_ type: T.Type, for request: URLRequest) async throws -> T {\n let (data, response) = try await URLSession.shared.data(for: request)\n\n guard let response = response as? HTTPURLResponse else {\n throw URLError(.badServerResponse)\n }\n\n guard 200 ... 299 ~= response.statusCode else {\n throw ApiError.failure(response.statusCode, data)\n }\n\n return try JSONDecoder().decode(T.self, from: data)\n }\n\n var request: URLRequest = {\n let url = URL(string: "https://httpbin.org/anything")!\n var request = URLRequest(url: url)\n request.httpMethod = "POST"\n request.httpBody = "[1,2,3,4,5]".data(using: .utf8)\n request.addValue("application/json", forHTTPHeaderField: "Content-Type")\n request.addValue("application/json", forHTTPHeaderField: "Accept")\n\n return request\n }()\n}\n\nenum ApiError: Error {\n case failure(Int, Data)\n}\nRun Code Online (Sandbox Code Playgroud)\n
的替代品DispatchQueue.main.async { foo.bar() }是:
Task { @MainActor in
print(Thread.current.isMainThread) // "true"
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7815 次 |
| 最近记录: |