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

Swift使用DispatchSemaphore循环调用依赖型API导致应用冻结如何解决

问题根因

  • 你的代码直接在网络请求的回调队列(绝大多数iOS网络库默认会把请求回调切到主队列)执行DispatchSemaphore.wait(),该方法会直接阻塞当前队列,而后续的signal()需要等待网络请求完成后同样在该队列执行回调才能触发,直接造成死锁。主队列被阻塞后所有UI响应、队列任务都无法执行,就会出现应用无响应的问题。
  • 同时你混用了DispatchGroup和DispatchSemaphore,逻辑冗余,循环内每次请求都调用completion(indexValue)也不符合常规的全量请求完成后统一回调的需求。

解决方案

方案1:调整Semaphore执行队列,避免阻塞主队列

把循环执行的逻辑丢到全局后台队列,不在主队列执行wait操作即可解决死锁问题:

func DownloadInformation(urlString: String, device: String, indexValue: Int, completion: @escaping (_ requestIndex: Int?) -> (Void)) {
    ApiService.sharedInstance.DownloadHealthInsightDetails(ID: self.equipmentAnalaysisArr[indexValue].id) { [self] (serverResponse, error, cardID) -> (Void) in
        if let serverResponse = serverResponse, error == nil {
            let insightCycleList = serverResponse as? NSDictionary
            if insightCycleList?["error"] == nil {
                if let cycle = insightCycleList?["cycles"] as? [AnyObject], !cycle.isEmpty {
                    // 将串行执行逻辑放到后台队列,避免阻塞主队列
                    DispatchQueue.global().async {
                        let dispatchSemaphore = DispatchSemaphore(value: 0)
                        let validCount = min(cycle.count, 5)
                        for i in 0..<validCount {
                            let element = cycle[i]
                            self.DownloadCycleData(urlString: blob_url , cycleDownloadCompletion: {_ in
                                dispatchSemaphore.signal()
                            })
                            dispatchSemaphore.wait()
                        }
                        // 所有请求完成后切回主队列执行回调
                        DispatchQueue.main.async {
                            completion(indexValue)
                            print("done")
                        }
                    }
                } else {
                    self.ShowMessage(index: indexValue, message: self.message)
                }
            } else {
                self.ShowMessage(index: indexValue, message: self.message)
            }
        }
    }
}

方案2:递归实现串行请求(更推荐,无死锁风险)

不需要依赖信号量,在上一个请求的回调里触发下一个请求,逻辑更清晰,不存在队列阻塞风险:

func DownloadInformation(urlString: String, device: String, indexValue: Int, completion: @escaping (_ requestIndex: Int?) -> (Void)) {
    ApiService.sharedInstance.DownloadHealthInsightDetails(ID: self.equipmentAnalaysisArr[indexValue].id) { [self] (serverResponse, error, cardID) -> (Void) in
        if let serverResponse = serverResponse, error == nil {
            let insightCycleList = serverResponse as? NSDictionary
            if insightCycleList?["error"] == nil {
                if let cycle = insightCycleList?["cycles"] as? [AnyObject], !cycle.isEmpty {
                    let validCount = min(cycle.count, 5)
                    // 递归请求函数
                    func requestCycle(index: Int) {
                        guard index < validCount else {
                            // 所有请求完成后回调
                            DispatchQueue.main.async {
                                completion(indexValue)
                                print("done")
                            }
                            return
                        }
                        let element = cycle[index]
                        self.DownloadCycleData(urlString: blob_url, cycleDownloadCompletion: { _ in
                            // 上一个请求完成,触发下一个请求
                            requestCycle(index: index + 1)
                        })
                    }
                    requestCycle(index: 0)
                } else {
                    self.ShowMessage(index: indexValue, message: self.message)
                }
            } else {
                self.ShowMessage(index: indexValue, message: self.message)
            }
        }
    }
}

方案3:iOS 15+ 用Swift Concurrency实现

如果项目最低支持iOS 15及以上版本,直接用async/await改写网络请求,串行逻辑写起来和同步代码一样简洁,不需要依赖信号量或者递归:

只需先把DownloadHealthInsightDetails和DownloadCycleData方法改为async版本,或者用withCheckedThrowingContinuation包装原有闭包版本的方法即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 01:30:02