繁体   English   中英

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

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

我正在关注斯坦福的 CS193p 为 iOS 在线课程开发应用程序。

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

“截至 WWDC 2021,GCD 大部分已被 Swift 新的内置异步 API 所取代”。

所以我想了解讲座中的代码在更新它以使用这个新的 API 后的样子。

看完 Apple 的 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)
            }
        }
    }
}

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

如果你真的打算做一些缓慢和同步的事情, 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。

暂无
暂无

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

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