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

Swift 4中URLSession请求后全局字典未更新问题咨询

问题分析与解决方案

嘿,这个问题我太熟悉了——本质上是异步任务的执行时机在搞鬼!让我一步步给你拆解清楚:

为什么循环结束后字典没更新?

你调用的URLSession.shared.dataTask(with:)是异步执行的:它会把网络请求放到后台线程处理,不会阻塞当前主线程,也就是你的for循环会继续往下跑,不会等请求返回结果。

你的代码实际执行顺序是这样的:

  1. 循环遍历所有routeid,依次发起5个网络请求,但这些请求都在后台跑,还没返回结果
  2. 循环快速结束,立刻执行print(prioritized_routes_with_stops)——这时候所有闭包都还没触发,字典自然还是初始状态
  3. 过了一会儿,网络请求陆续返回,闭包才被执行,这时候字典才会被更新

而你在闭包里能看到正确值,是因为闭包是在网络请求完成后才触发的,这时候数据已经拿到、字典也更新了,但这时候循环早就跑完,之前的print已经输出过旧值了。

怎么解决这个问题?

要等所有异步请求都完成后再访问更新后的字典,你可以用DispatchGroup来跟踪所有任务的完成状态,具体代码修改如下:

步骤1:修改循环部分,加入DispatchGroup

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 1. 初始化你的prioritized_routes_with_stops字典...
    
    // 创建一个DispatchGroup来跟踪所有异步任务
    let taskGroup = DispatchGroup()
    
    for routeid in prioritized_routes_with_stops.keys {
        taskGroup.enter() // 标记一个任务开始
        
        _ = prioritizedRoutesHelper(completion: { lst in
            // 注意:网络请求的闭包在后台线程执行,更新字典最好切回主线程(避免线程安全问题)
            DispatchQueue.main.async {
                self.prioritized_routes_with_stops[routeid] = lst
                taskGroup.leave() // 标记这个任务完成
            }
        }, routeid: routeid)
    }
    
    // 当所有任务都完成时,触发这个闭包
    taskGroup.notify(queue: .main) {
        print("所有路线数据更新完成:")
        print(self.prioritized_routes_with_stops)
        // 这里可以做后续的UI更新或数据处理
    }
}

步骤2:(可选)优化prioritizedRoutesHelper的错误处理

你的helper方法里有强制解包(dataAsString?.contains(...))!,很容易崩溃,建议改成安全的可选绑定:

func prioritizedRoutesHelper(completion: @escaping (((String, String), Int, Int)) -> (), routeid: String) {
    guard let stopId = user_dict[routeid]?.0 else {
        // 处理stopId不存在的情况
        completion((("a", ""), 0, 0))
        return
    }
    let formatted_url = "https://api.umd.io/v0/bus/routes/\(routeid)/arrivals/\(stopId)"
    guard let url = URL(string: formatted_url) else {
        completion((("a", ""), 0, 0))
        return
    }
    
    URLSession.shared.dataTask(with: url) { (data, response, err) in
        if let err = err {
            print("请求出错:\(err)")
            completion((("a", ""), 0, 0))
            return
        }
        guard let data = data else {
            completion((("a", ""), 0, 0))
            return
        }
        guard let dataAsString = String(data: data, encoding: .utf8) else {
            completion((("a", ""), 0, 0))
            return
        }
        
        if dataAsString.contains("dirTitleBecauseNoPredictions") {
            let temp = (("a", ""), 0, 0)
            completion(temp)
        } else {
            // 处理有预测数据的情况
            // ...
        }
    }.resume()
}

额外提醒:线程安全

因为URLSession的闭包在后台线程执行,如果你之后要根据这个字典更新UI,一定要确保所有字典的修改和UI操作都在主线程(用DispatchQueue.main.async包裹),否则可能会出现界面错乱或者数据竞争的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:07:23