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

Swift中PLP页面异步调用库存API并实时更新UI的问题求助

问题解决:PLP页面异步批量调用库存API并实时更新UI

问题分析

你遇到的核心问题有两个:

  • 使用DispatchSemaphore时页面跳转崩溃:大概率是页面销毁后,API回调仍尝试访问已释放的self或信号量,导致野指针访问;同时循环里的wait会阻塞线程,若页面销毁时仍有未完成的wait/signal操作,容易引发死锁或崩溃。
  • 使用OperationQueue/DispatchGroup无法实时更新UI:并非这两种方案本身限制,而是你的实现逻辑有误——这两种方案完全支持单个API响应返回后立刻更新UI,不需要等待全部请求完成。

正确实现方案

方案1:优化OperationQueue实现(推荐)

通过BlockOperation封装异步API请求,确保每个操作能正确跟踪API生命周期,同时在回调中实时更新UI,页面销毁时取消所有未完成操作避免崩溃。

class PLPViewController: UIViewController {
    private let stockQueue = OperationQueue()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        stockQueue.qualityOfService = .background
        stockQueue.maxConcurrentOperationCount = 5 // 控制并发请求数
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        stockQueue.cancelAllOperations() // 页面销毁时取消所有未完成操作
    }
    
    // 产品列表回调处理
    func setupProductList() {
        productListView.sendCBRClosure = { [weak self] cbrCodeArray in
            guard let self = self else { return }
            for cbrCode in cbrCodeArray {
                let operation = BlockOperation { [weak self, weak operation] in
                    guard let self = self, let operation = operation, !operation.isCancelled else { return }
                    
                    self.getStockDetail(cbrCode: cbrCode) { [weak self] qtyStr in
                        guard let self = self, !operation.isCancelled else { return }
                        // 回到主线程更新对应产品的UI
                        DispatchQueue.main.async {
                            self.updateStockUI(for: cbrCode, quantity: qtyStr)
                        }
                    }
                }
                self.stockQueue.addOperation(operation)
            }
        }
    }
    
    // 库存API请求方法(示例)
    private func getStockDetail(cbrCode: String, completion: @escaping (String) -> Void) {
        // 实际异步API请求逻辑
        URLSession.shared.dataTask(with: URL(string: "your-stock-api/\(cbrCode)")!) { data, _, error in
            // 解析数据后返回结果
            guard let data = data, error == nil else {
                completion("0")
                return
            }
            // 假设解析出库存数量字符串
            let quantity = try? JSONSerialization.jsonObject(with: data) as? String ?? "0"
            completion(quantity!)
        }.resume()
    }
    
    // 更新对应产品的库存UI
    private func updateStockUI(for cbrCode: String, quantity: String) {
        // 根据cbrCode找到对应产品cell,更新库存显示
    }
}

方案2:修复DispatchSemaphore实现

如果坚持用信号量,需要严格管理生命周期,避免页面销毁后出现非法访问:

class PLPViewController: UIViewController {
    private var semaphore: DispatchSemaphore? = DispatchSemaphore(value: 4)
    private let workQueue = DispatchQueue.global(qos: .background)
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        semaphore = nil // 页面销毁时置空信号量,终止后续操作
        // 同时取消所有未完成的API请求(若API支持取消)
    }
    
    func setupProductList() {
        productListView.sendCBRClosure = { [weak self] cbrCodeArray in
            guard let self = self, let semaphore = self.semaphore else { return }
            self.workQueue.async { [weak self] in
                for cbrCode in cbrCodeArray {
                    guard let self = self, let semaphore = self.semaphore else { break }
                    
                    semaphore.wait()
                    self.getStockDetail(cbrCode: cbrCode) { [weak self] qtyStr in
                        defer { self?.semaphore?.signal() }
                        guard let self = self else { return }
                        DispatchQueue.main.async {
                            self.updateStockUI(for: cbrCode, quantity: qtyStr)
                        }
                    }
                }
            }
        }
    }
    
    // getStockDetail和updateStockUI方法同方案1
}

关键注意事项

  • 生命周期管理:所有闭包必须使用[weak self],回调中先检查self是否存在;页面销毁时务必取消所有未完成的异步任务。
  • UI更新线程:所有UI操作必须通过DispatchQueue.main.async执行,避免跨线程操作错误。
  • 并发数控制:通过队列的maxConcurrentOperationCount或信号量初始值控制并发请求数,避免触发API频率限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 05:24:10