你问:
我还能用吗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
}
}
And then
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
List {
...
}
.refreshable {
await viewModel.fetchData()
}
}
}
(注意,我做了fetchData
an async
方法和await
它在里面refreshable
以便旋转器准确反映何时async
进程正在运行。)
观看 WWDC 2021 视频Swift 并发:更新示例应用程序 https://developer.apple.com/videos/play/wwdc2021/10194/。诚然,这说明了 UIKit 应用程序的转变,但包括以下示例@MainActor
and MainActor.run
.
注意,同时@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)
}