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

Swift多线程问题:主线程异步调用先于全局队列任务完成

问题分析与解决方案

你的代码问题很典型——没等异步网络请求完成就提前执行了UI更新逻辑。

为什么原来的代码会失败?

requestAds()是耗时的异步API请求,它不会阻塞当前的全局队列。所以你写完requestAds()后立刻调用DispatchQueue.main.async,这部分UI更新代码会在requestAds()还没拿到数据、甚至还没完成请求的时候就跑起来了。这时候getAds()自然拿不到任何数据,ads数组为空,表格刷新后当然什么都没有。

正确的写法:用回调闭包等待请求完成

首先你需要改造requestAds(),给它加一个完成回调,让它在请求+解析完成后通知我们拿到的数据:

func requestAds(completion: @escaping ([Ads]) -> Void) {
    // 替换成你的实际API请求逻辑,这里用URLSession举例
    let adRequestURL = URL(string: "你的广告API地址")!
    URLSession.shared.dataTask(with: adRequestURL) { data, response, error in
        // 处理请求错误
        guard let data = data, error == nil else {
            print("请求失败: \(error?.localizedDescription ?? "未知错误")")
            completion([]) // 失败时返回空数组,也可以根据需求返回错误信息
            return
        }
        
        // 解析数据为[Ads]数组
        do {
            let fetchedAds = try JSONDecoder().decode([Ads].self, from: data)
            completion(fetchedAds)
        } catch {
            print("数据解析失败: \(error)")
            completion([])
        }
    }.resume()
}

然后在viewDidLoad里,把UI更新的逻辑放到回调闭包中,确保只有当数据真正拿到后才执行:

var ads = [Ads]()

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 发起广告请求,拿到数据后再更新UI
    requestAds { [weak self] fetchedAds in
        // 回到主线程更新UI(UI操作必须在主线程执行)
        DispatchQueue.main.async {
            guard let self = self else { return } // 避免循环引用
            self.ads = fetchedAds
            print("获取到广告数量: \(self.ads.count)")
            self.adsTableView.reloadData()
        }
    }
}

额外优化提示

  • 避免多余的全局队列:URLSession的dataTask本身就是在后台队列执行的,所以不需要额外套DispatchQueue.global(qos: .userInteractive).async,直接调用requestAds即可。
  • 处理循环引用:用[weak self]捕获self,避免ViewController因为闭包引用而无法释放。
  • 错误状态反馈:如果请求或解析失败,除了打印日志,最好在UI上给用户一个提示(比如显示“暂无广告”的占位视图),提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 07:54:40