如何从 GCD 转换为 Swift async/await(DispatchQueue)?



我正在学习斯坦福大学的CS193p开发iOS应用程序在线课程。

它使用Grand Central Dispatch(GCD)API进行多线程演示。 但他们指出,

"从 WWDC 2021 开始,GCD 已大部分被 Swift 新的内置异步 API 取代"。

因此,我想了解讲座中的代码在更新以使用此新 API 后的外观。

看完苹果的WWDC视频后,在我看来
DispatchQueue.global(qos: .userInitiated).async { }在这个新的异步API中被替换为Task { }Task(priority: .userInitiated) {},但我不确定,DispatchQueue.main.async { }被替换了什么?

所以,我的问题是:

  1. 我是否正确假设,DispatchQueue.global(qos: .userInitiated).async { }已被替换为Task(priority: .userInitiated) {}
  2. DispatchQueue.main.async { }被什么取代了?

请帮忙,我想学习这个新的异步等待 API。

以下是讲座中的代码,使用旧的 GCD API:

DispatchQueue.global(qos: .userInitiated).async {
let imageData = try? Data(contentsOf: url)
DispatchQueue.main.async { [weak self] in
if self?.emojiArt.background == EmojiArtModel.Background.url(url) {
self?.backgroundImageFetchStatus = .idle
if imageData != nil {
self?.backgroundImage = UIImage(data: imageData!)
}
// L12 note failure if we couldn't load background image
if self?.backgroundImage == nil {
self?.backgroundImageFetchStatus = .failed(url)
}
}
}
}

整个函数(如果您需要查看更多代码):

private func fetchBackgroundImageDataIfNecessary() {
backgroundImage = nil
switch emojiArt.background {
case .url(let url):
// fetch the url
backgroundImageFetchStatus = .fetching
DispatchQueue.global(qos: .userInitiated).async {
let imageData = try? Data(contentsOf: url)
DispatchQueue.main.async { [weak self] in
if self?.emojiArt.background == EmojiArtModel.Background.url(url) {
self?.backgroundImageFetchStatus = .idle
if imageData != nil {
self?.backgroundImage = UIImage(data: imageData!)
}
// L12 note failure if we couldn't load background image
if self?.backgroundImage == nil {
self?.backgroundImageFetchStatus = .failed(url)
}
}
}
}
case .imageData(let data):
backgroundImage = UIImage(data: data)
case .blank:
break
}
}

如果你真的要做一些缓慢而同步的事情,Task.detached更接近于GCD调度到全局队列。如果您只使用Task(priority: ...) { ... }则由并发系统自行决定在哪个线程上运行它。(仅仅因为您指定了较低的priority并不能保证它可能不会在主线程上运行。

例如:

func fetchAndUpdateUI(from url: URL) {
Task.detached {                          // or specify a priority with `Task.detached(priority: .background)`
let data = try Data(contentsOf: url)
let image = UIImage(data: data)
await self.updateUI(with: image)
}
}

如果要在主线程上执行 UI 更新,而不是将其调度回主队列,只需将@MainActor修饰符添加到更新 UI 的方法中:

@MainActor
func updateUI(with image: UIImage?) async {
imageView.image = image
}

话虽如此,这是一种非常不寻常的模式(同步执行网络请求并创建一个分离的任务以确保您不会阻塞主线程)。我们可能会使用URLSession的新异步data(from:delegate:)方法来异步执行请求。它提供更好的错误处理、更好的可配置性、参与结构化并发,并且是可取消的。

简而言之,与其为旧的 GCD 模式寻找一对一的类似物,不如尽可能使用 Apple 提供的并发 API。


FWIW,除了上面显示的@MainActor模式(作为调度到主队列的替代品)之外,您还可以执行以下操作:

await MainActor.run {
…
}

这大致类似于调度到主队列。在 WWDC 2021 视频 Swift 并发:更新示例应用中,他们说:

在 Swift 的并发模型中,有一个称为 main actor 的全局参与者,它协调主线程上的所有操作。我们可以用调用MainActorrun函数来替换我们的DispatchQueue.main.async。这需要一段代码才能在MainActor上运行。。

但他接着说:

我可以用@MainActor注释函数。这将要求调用方在运行此函数之前切换到主参与者。...现在我们已经把这个功能放在了主要演员身上,严格来说,我们不再需要这个MainActor.run了。

相关内容

  • 没有找到相关文章

最新更新