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

Swift4+Xcode9+GCD:图片加载完成后仅调用一次setupScrollView

解决GCD异步图片下载后统一更新ScrollView的问题

我来帮你搞定这个问题!你遇到的核心痛点是如何等待所有异步图片下载任务完成后,只调用一次setupScrollView()来更新UI——DispatchGroup就是专门解决这类多异步任务同步的工具,完美适配你的场景。

核心思路

用DispatchGroup追踪每一个图片下载任务:

  1. 每个下载任务启动前调用enter(),告诉group“我要开始干活了”;
  2. 任务结束(无论成功失败)必须调用leave(),告诉group“我干完了”;
  3. 当group里所有任务都完成时,通过notify回到主线程,统一执行UI更新操作。

修改后的完整代码

首先把imagesForPost改成类的实例变量(方便后续setupScrollView直接使用),然后改造下载函数:

// 定义为类的实例变量,保存下载好的图片
var imagesForPost: [UIImage] = []

func downloadImages(arrayOfURLS: [URL]) {
    // 先清空旧数据,避免和上次下载的图片混淆
    imagesForPost.removeAll()
    let downloadGroup = DispatchGroup()
    print("Ссылок столько - ", arrayOfURLS.count)
    
    for url in arrayOfURLS {
        downloadGroup.enter() // 标记一个新任务开始
        
        // 推荐用URLSession异步下载(比Data(contentsOf:)更高效,非阻塞)
        let downloadTask = URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
            // 用defer确保无论任务成功/失败,都会调用leave(),避免group永久等待
            defer {
                downloadGroup.leave()
            }
            
            guard let self = self, let imageData = data, error == nil, let image = UIImage(data: imageData) else {
                print("图片下载失败:\(error?.localizedDescription ?? "未知错误")")
                return
            }
            
            // 线程安全地更新图片数组(避免多线程同时修改导致数据竞争)
            DispatchQueue.global(qos: .userInitiated).sync {
                self.imagesForPost.append(image)
            }
        }
        downloadTask.resume()
    }
    
    // 所有任务完成后,回到主线程调用setupScrollView
    downloadGroup.notify(queue: .main) { [weak self] in
        guard let self = self else { return }
        self.setupScrollView()
        print("所有图片下载完成,共加载了\(self.imagesForPost.count)张有效图片")
    }
}

关键细节说明

  • 线程安全:因为多个下载任务可能同时修改imagesForPost,所以用串行队列同步修改操作,避免数组出现数据错乱;
  • 避免永久等待:用defer包裹downloadGroup.leave(),确保无论下载成功、失败还是抛出异常,都会通知group任务完成;
  • UI操作必须在主线程:notify指定queue: .main,保证setupScrollView()在主线程执行,符合iOS UI更新的规则;
  • 替换阻塞式下载:放弃Data(contentsOf:)这种同步阻塞的方式,改用URLSession异步下载,不会占用全局队列的线程资源,性能更优。

这样修改后,就能确保只有当所有图片下载任务都完成时,才会调用一次setupScrollView(),既不会出现无图片加载的情况,也不会重复加载图片。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 03:42:23