Alamofire请求后追加数组外部无法访问的问题排查
解决Alamofire异步请求后数组数据无法外部访问的问题
嘿,我一眼就看出问题出在哪了——你搞混了异步操作的执行顺序,而且完成处理程序的调用时机完全错了!
核心问题分析
Alamofire的网络请求是异步执行的:当你调用Alamofire.request()后,代码会立刻继续往下走,不会等网络请求返回结果。
看你最初的代码,外部的UI操作(showCount、displayItemsAroundCurrentLocation)会先于闭包里的数组追加代码执行,这时候establishments数组自然还是空的。
再看你EDIT3里的完成处理程序,你把completion()放在了Alamofire.request()的外面,而不是请求完成的闭包内部!这意味着你还是在网络请求没完成、数组还没更新的时候就触发了回调,结果当然还是0。
正确的修复方案
1. 修正完成处理程序的调用时机
把completion()移到网络请求的闭包里面,确保只有当数据解析完成、数组更新后才通知外部:
func tryingWithCompletionHandler(jsonURLString: String, headers: HTTPHeaders, establishment: Establishment, completion : ()->()) { Alamofire.request(jsonURLString, headers: headers).responseJSON { response in do { // 先做可选值安全处理,避免强制解包崩溃 guard let data = response.data else { print("No data received from request") completion() return } let establishmentsFromJSON = try JSONDecoder().decode(EstablishmentsFromJSON.self, from: data) if !establishmentsFromJSON.establishments.isEmpty { // 重要:如果Establishment是struct(值类型),必须创建可变副本再修改 var mutableEstablishment = establishment mutableEstablishment.hygieneRating = establishmentsFromJSON.establishments.first?.RatingValue mutableEstablishment.hygieneRatingKey = establishmentsFromJSON.establishments.first?.RatingKey mutableEstablishment.hygieneRatingDate = ISO8601DateFormatter().date(from: establishmentsFromJSON.establishments[0].RatingDate) mutableEstablishment.hygieneRatingPending = establishmentsFromJSON.establishments.first?.NewRatingPending mutableEstablishment.localAuthorityName = establishmentsFromJSON.establishments.first?.LocalAuthorityName mutableEstablishment.localAuthorityWebsite = establishmentsFromJSON.establishments.first?.LocalAuthorityWebSite mutableEstablishment.localAuthorityEmail = establishmentsFromJSON.establishments.first?.LocalAuthorityEmailAddress self.establishments.append(mutableEstablishment) } // 不管有没有解析到数据,都要调用completion completion() } catch { print("Parsing error occurred:") print(error) // 出错时也要调用completion,避免回调永远不执行 completion() } } // 原来这里的completion()要彻底删掉! }
2. 调用时切换到主线程更新UI
Alamofire的responseJSON闭包默认在后台线程执行,更新UI必须回到主线程:
self.tryingWithCompletionHandler(jsonURLString: jsonURLString, headers: headers, establishment: establishment, completion: { DispatchQueue.main.async { self.showCount(self.establishments.count) print("place count: \(self.establishments.count)") self.arView.displayItemsAroundCurrentLocation(currentLocation: currentPlace, establishments: self.establishments) } })
3. 额外注意:值类型的坑
如果Establishment是struct(值类型),直接修改传入的establishment对象是没用的——因为值类型传递的是副本,你修改的只是闭包里的临时副本,不会影响原对象。所以上面的代码里创建了mutableEstablishment可变副本,修改后再追加到数组。如果是class(引用类型),这一步可以省略,但也要注意线程安全。
为什么之前的尝试没用?
- 你一开始的代码没考虑异步顺序,UI操作先执行了
- 完成处理程序的错误在于
completion()调用太早,没等异步请求完成就触发了回调
只要把completion()移到闭包内部,确保数组更新后再通知外部,问题就迎刃而解了。
内容的提问来源于stack exchange,提问作者nopassport1
相关产品推荐
相关产品推荐

