Swift 4中URLSession请求后全局字典未更新问题咨询
问题分析与解决方案
嘿,这个问题我太熟悉了——本质上是异步任务的执行时机在搞鬼!让我一步步给你拆解清楚:
为什么循环结束后字典没更新?
你调用的URLSession.shared.dataTask(with:)是异步执行的:它会把网络请求放到后台线程处理,不会阻塞当前主线程,也就是你的for循环会继续往下跑,不会等请求返回结果。
你的代码实际执行顺序是这样的:
- 循环遍历所有routeid,依次发起5个网络请求,但这些请求都在后台跑,还没返回结果
- 循环快速结束,立刻执行
print(prioritized_routes_with_stops)——这时候所有闭包都还没触发,字典自然还是初始状态 - 过了一会儿,网络请求陆续返回,闭包才被执行,这时候字典才会被更新
而你在闭包里能看到正确值,是因为闭包是在网络请求完成后才触发的,这时候数据已经拿到、字典也更新了,但这时候循环早就跑完,之前的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
相关产品推荐
相关产品推荐

