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

Swift 4中如何实现两个并发API调用并在全部完成后更新UI?

嘿,我来帮你搞定这个并发API调用的问题~你的思路方向是对的,用OperationQueue来做并发没问题,但原代码缺少了跟踪两个API都成功完成的逻辑,所以没法触发停止指示器和刷新表格的操作。我给你两种靠谱的实现方案,你可以根据需求选:

方案一:用DispatchGroup(简洁直观,推荐)

DispatchGroup是GCD里专门用来跟踪多个异步任务完成的工具,非常适合这种场景。我们可以用它来监听两个API都成功完成,然后统一处理UI更新:

func downloadDetails() {
    // 先在主线程启动活动指示器(UI操作必须在主线程!)
    DispatchQueue.main.async {
        self.activityIndicator.startAnimating()
        self.tableView.isUserInteractionEnabled = false // 可选:防止用户操作
    }
    
    // 定义两个变量存储接口返回的数据(根据你的实际类型调整)
    var fetchedAData: Any?
    var fetchedBData: Any?
    
    let group = DispatchGroup()
    
    // 第一个API调用
    group.enter()
    WebServiceManager.getAData(format: A) { data, error in
        defer { group.leave() } // 不管成功失败,都告诉group任务结束
        guard let successData = data, error == nil else {
            // 这里可以处理A接口失败的情况,比如打印日志或提示用户
            print("Failed to fetch A data: \(error?.localizedDescription ?? "Unknown error")")
            return
        }
        fetchedAData = successData
        // 这里可以做A数据的预处理,比如转模型
    }
    
    // 第二个API调用
    group.enter()
    WebServiceManager.getBData(format: B) { data, error in
        defer { group.leave() }
        guard let successData = data, error == nil else {
            print("Failed to fetch B data: \(error?.localizedDescription ?? "Unknown error")")
            return
        }
        fetchedBData = successData
        // 预处理B数据
    }
    
    // 当两个任务都结束时,执行统一逻辑
    group.notify(queue: .main) { [weak self] in
        guard let self = self, fetchedAData != nil, fetchedBData != nil else {
            // 至少一个接口失败,停止指示器并提示用户
            self?.activityIndicator.stopAnimating()
            self?.tableView.isUserInteractionEnabled = true
            // 可选:显示错误弹窗
            return
        }
        
        // 两个接口都成功!停止指示器+刷新表格
        self.activityIndicator.stopAnimating()
        self.tableView.isUserInteractionEnabled = true
        self.tableView.reloadData()
    }
}

方案二:用OperationQueue+线程安全计数(适合复杂场景)

如果你需要更灵活的任务管理(比如中途取消任务、添加依赖),可以继续用OperationQueue,但要加一个线程安全的计数器来跟踪成功次数:

func downloadDetails() {
    DispatchQueue.main.async {
        self.activityIndicator.startAnimating()
        self.tableView.isUserInteractionEnabled = false
    }
    
    let operationQueue = OperationQueue()
    operationQueue.maxConcurrentOperationCount = 2 // 允许同时跑两个任务
    
    var successCount = 0
    let countLock = NSLock() // 保证多线程下计数安全
    var fetchedAData: Any?
    var fetchedBData: Any?
    
    let operation1 = BlockOperation { [weak self] in
        WebServiceManager.getAData(format: A) { data, error in
            guard let successData = data, error == nil else {
                // A接口失败,检查是否两个都失败了,是的话停止指示器
                countLock.lock()
                let bothFailed = successCount == 0
                countLock.unlock()
                
                if bothFailed {
                    DispatchQueue.main.async {
                        self?.activityIndicator.stopAnimating()
                        self?.tableView.isUserInteractionEnabled = true
                    }
                }
                return
            }
            
            fetchedAData = successData
            // 更新成功计数
            countLock.lock()
            successCount += 1
            let allSuccess = successCount == 2
            countLock.unlock()
            
            if allSuccess {
                DispatchQueue.main.async {
                    self?.activityIndicator.stopAnimating()
                    self?.tableView.isUserInteractionEnabled = true
                    self?.tableView.reloadData()
                }
            }
        }
    }
    
    let operation2 = BlockOperation { [weak self] in
        WebServiceManager.getBData(format: B) { data, error in
            guard let successData = data, error == nil else {
                countLock.lock()
                let bothFailed = successCount == 0
                countLock.unlock()
                
                if bothFailed {
                    DispatchQueue.main.async {
                        self?.activityIndicator.stopAnimating()
                        self?.tableView.isUserInteractionEnabled = true
                    }
                }
                return
            }
            
            fetchedBData = successData
            countLock.lock()
            successCount += 1
            let allSuccess = successCount == 2
            countLock.unlock()
            
            if allSuccess {
                DispatchQueue.main.async {
                    self?.activityIndicator.stopAnimating()
                    self?.tableView.isUserInteractionEnabled = true
                    self?.tableView.reloadData()
                }
            }
        }
    }
    
    operationQueue.addOperations([operation1, operation2], waitUntilFinished: false)
}

关键注意点:

  • UI操作必须在主线程:活动指示器的启动/停止、表格刷新都要放在DispatchQueue.main.async里,否则会崩溃。
  • 内存泄漏防护:用[weak self]捕获self,避免循环引用(因为网络回调会持有self,而self持有OperationQueue/DispatchGroup)。
  • 错误处理:不要忽略错误情况,比如其中一个接口失败时,要停止指示器并给用户反馈。
  • 线程安全:如果用计数器,一定要加锁(比如NSLock),因为两个回调可能同时更新计数,导致线程冲突。

这样修改后,就能实现你要的“两个API并发执行,都成功后停止指示器+刷新表格”的需求啦~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:16:35