我正在学习斯坦福大学的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 { }
被替换了什么?
所以,我的问题是:
- 我是否正确假设,
DispatchQueue.global(qos: .userInitiated).async { }
已被替换为Task(priority: .userInitiated) {}
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 的全局参与者,它协调主线程上的所有操作。我们可以用调用
MainActor
的run
函数来替换我们的DispatchQueue.main.async
。这需要一段代码才能在MainActor
上运行。。
但他接着说:
我可以用
@MainActor
注释函数。这将要求调用方在运行此函数之前切换到主参与者。...现在我们已经把这个功能放在了主要演员身上,严格来说,我们不再需要这个MainActor.run
了。