简体   繁体   English

如何从 GCD(DispatchQueue)转换为 Swift 异步/等待?

[英]How can I convert to Swift async/await from GCD (DispatchQueue)?

I am following Stanfords' CS193p Developing Apps for iOS online course.我正在关注斯坦福的 CS193p 为 iOS 在线课程开发应用程序。

It is using the Grand Central Dispatch (GCD) API for a demo of multithreading.它使用 Grand Central Dispatch (GCD) API 进行多线程演示。 But they noted, that但他们注意到,

"GCD has been mostly replaced by Swift's new built-in async API as of WWDC 2021". “截至 WWDC 2021,GCD 大部分已被 Swift 新的内置异步 API 所取代”。

So I wanted to learn how the code from the Lecture would look like after updating it to use this new API.所以我想了解讲座中的代码在更新它以使用这个新的 API 后的样子。

After watching Apple's WWDC videos, it seems to me like看完 Apple 的 WWDC 视频后,我觉得
DispatchQueue.global(qos: .userInitiated).async { } is replaced in this new async API with Task { } or Task(priority: .userInitiated) {} , but I'm not sure, what has DispatchQueue.main.async { } been replaced with? DispatchQueue.global(qos: .userInitiated).async { }在这个新的异步 API 中替换为Task { }Task(priority: .userInitiated) {} ,但我不确定DispatchQueue.main.async { }被替换为?

So, my questions are:所以,我的问题是:

  1. Am I correctly assuming, that DispatchQueue.global(qos: .userInitiated).async { } has been replaced with Task(priority: .userInitiated) {}我是否正确假设DispatchQueue.global(qos: .userInitiated).async { }已被替换为Task(priority: .userInitiated) {}
  2. What has DispatchQueue.main.async { } been replaced with? DispatchQueue.main.async { }被什么替换了?

Please help, I want to learn this new async-await API.请帮忙,我想学习这个新的异步等待 API。

Here's the code from the Lecture, using old GCD 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)
            }
        }
    }
}

The whole function (in case you need to see more code):整个 function (以防您需要查看更多代码):

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
    }
}

If you really are going to do something slow and synchronous, Task.detached is a closer analog to GCD's dispatching to a global queue.如果你真的打算做一些缓慢和同步的事情, Task.detached更接近于 GCD 对全局队列的分派。 If you just use Task(priority: ...) {... } you are leaving it to the discretion of the concurrency system to decide which thread to run it on.如果您只使用Task(priority: ...) {... }您将由并发系统自行决定在哪个线程上运行它。 (And just because you specify a lower priority does not guarantee that it might not run on the main thread.) (并且仅仅因为您指定了较低的priority并不能保证它可能不会在主线程上运行。)

For example:例如:

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)
    }
}

And if you want to do the UI update on the main thread, rather than dispatching it back to the main queue, you would simply add the @MainActor modifier to the method that updates the UI:如果您想在主线程上进行 UI 更新,而不是将其分派回主队列,您只需将@MainActor修饰符添加到更新 UI 的方法中:

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

That having been said, this is a pretty unusual pattern (doing the network request synchronously and creating a detached task to make sure you don't block the main thread).话虽如此,这是一种非常不寻常的模式(同步执行网络请求并创建一个分离的任务以确保您不会阻塞主线程)。 We would probably use URLSession 's new asynchronous data(from:delegate:) method to perform the request asynchronously.我们可能会使用URLSession的新异步data(from:delegate:)方法来异步执行请求。

In short, rather than looking for one-to-one analogs for the old GCD patterns, use the concurrent API that Apple has provided where possible.简而言之,与其为旧的 GCD 模式寻找一对一的类似物,不如尽可能使用 Apple 提供的并发 API。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM