You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Swift async/await模拟可同步/异步返回的CompletionHandler风格代码

Swift并发中用async/await实现带缓存的图片加载

你完全不需要拆分同步和异步两个方法,一个async函数就能实现和原来CompletionHandler完全一致的逻辑——同步检查缓存、有就立即返回,无则异步加载后更新UI,还能顺便解决线程安全问题。

核心原理

Swift的async函数并非强制异步执行:如果函数内部没有遇到await挂起点,它会同步执行当前上下文的代码。也就是说,当你在主线程调用async函数时,检查缓存的逻辑会直接在主线程完成,有缓存就立刻返回结果,完全不会有线程切换的开销。

正确实现(带线程安全)

用Actor管理缓存是Swift并发里最安全的方式,避免多线程读写缓存的竞态条件:

actor ImageCache {
    private var cachedImages: [URL: UIImage] = [:]
    
    func fetchImage(url: URL) async throws -> UIImage {
        // 同步检查缓存:当前在调用者上下文执行(主线程调用就在主线程)
        if let image = cachedImages[url] {
            return image
        }
        
        // 异步加载:遇到await自动挂起,切换到后台线程执行网络请求
        let (data, _) = try await URLSession.shared.data(from: url)
        guard let image = UIImage(data: data) else {
            throw NSError(domain: "ImageLoader", code: 1, userInfo: [NSLocalizedDescriptionKey: "图片解析失败"])
        }
        
        // 存入缓存(Actor自动保证线程安全)
        cachedImages[url] = image
        return image
    }
}

在UICollectionView中调用

在主线程的cellForItemAt方法里直接使用,效果和原来的CompletionHandler完全一致:

// 全局或类内初始化缓存实例
let imageCache = ImageCache()

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCell", for: indexPath) as! ImageCell
    let imageUrl = yourImageUrls[indexPath.item]
    
    // 先设置占位图(可选,优化体验)
    cell.imageView.image = UIImage(named: "placeholder")
    
    // 启动Task处理异步逻辑
    Task { [weak cell] in
        do {
            let image = try await imageCache.fetchImage(url: imageUrl)
            // 回到主线程更新UI
            await MainActor.run {
                cell?.imageView.image = image
            }
        } catch {
            print("图片加载失败:\(error)")
            await MainActor.run {
                cell?.imageView.image = UIImage(named: "error")
            }
        }
    }
    
    return cell
}

为什么这能解决你的问题

  • 缓存命中时:fetchImage会同步返回图片,Task里的await瞬间完成,立刻执行MainActor.run更新UI,完全不会有延迟或闪烁,和原来的同步回调效果一样。
  • 缓存未命中时:自动切换到后台执行网络请求,完成后回到主线程更新UI,和原来的异步逻辑一致。
  • 不需要拆分同步/异步方法,代码更简洁,还通过Actor解决了原来代码中潜在的线程安全问题。

内容的提问来源于stack exchange,提问作者No No

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 10:46:16