URLSession异步请求后数组为空问题及解决方法咨询
问题原因
确实和异步执行方式有关。URLSession.shared.dataTask是异步任务,它会在后台线程发起网络请求,不会阻塞主线程。你调用oRedHatCVEs.jsonDataRequest()后,代码会立刻执行txtvJSON.text = ...,这时候网络请求还没完成,arrCVE还没被填充,所以显示元素数为0。
解决方案
方法1:闭包回调(最通用的异步处理方式)
修改请求函数,添加完成回调闭包,在数据加载完成后通知ViewController更新UI:
// 调整请求函数,增加完成回调 func jsonDataRequest(completion: @escaping ([CVE]?, Error?) -> Void) { guard let urlObj = URL(string: "https://access.redhat.com/hydra/rest/securitydata//cve.json?after=2022-12-26") else { completion(nil, NSError(domain: "InvalidURL", code: -1, userInfo: [NSLocalizedDescriptionKey: "URL格式错误"])) return } URLSession.shared.dataTask(with: urlObj) { (data, response, error) in if let error = error { completion(nil, error) return } guard let data = data else { completion(nil, NSError(domain: "NoData", code: -2, userInfo: [NSLocalizedDescriptionKey: "未获取到数据"])) return } do { // 直接解码为数组,无需遍历重新创建实例 let cveList = try JSONDecoder().decode([CVE].self, from: data) self.arrCVE = cveList completion(cveList, nil) } catch { completion(nil, error) } }.resume() }
ViewController中调用时,通过闭包获取数据并更新UI:
let oRedHatCVEs = RedHatCVEs() oRedHatCVEs.jsonDataRequest { [weak self] cveList, error in guard let self = self else { return } if let error = error { print("请求失败:\(error.localizedDescription)") return } // 必须在主线程更新UI DispatchQueue.main.async { self.txtvJSON.text = "Array elements: \(oRedHatCVEs.arrCVE.count)" } }
方法2:Combine框架(iOS 13+适用)
如果项目支持iOS 13及以上,可用Combine处理异步数据流:
import Combine class RedHatCVEs { var arrCVE : [CVE] = [] var cancellables = Set<AnyCancellable>() func jsonDataRequest() -> AnyPublisher<[CVE], Error> { guard let urlObj = URL(string: "https://access.redhat.com/hydra/rest/securitydata//cve.json?after=2022-12-26") else { return Fail(error: NSError(domain: "InvalidURL", code: -1, userInfo: [NSLocalizedDescriptionKey: "URL格式错误"])).eraseToAnyPublisher() } return URLSession.shared.dataTaskPublisher(for: urlObj) .map(\.data) .decode(type: [CVE].self, decoder: JSONDecoder()) .receive(on: DispatchQueue.main) .handleEvents(receiveOutput: { [weak self] cveList in self?.arrCVE = cveList }) .eraseToAnyPublisher() } }
ViewController中调用:
let oRedHatCVEs = RedHatCVEs() oRedHatCVEs.jsonDataRequest() .sink(receiveCompletion: { completion in if case .failure(let error) = completion { print("请求失败:\(error.localizedDescription)") } }, receiveValue: { [weak self] cveList in self?.txtvJSON.text = "Array elements: \(cveList.count)" }) .store(in: &oRedHatCVEs.cancellables)
额外优化建议
- 去掉代码中遍历重新创建
CVE实例的逻辑,直接self.arrCVE = jsonCVE即可完成数组填充,避免不必要的性能消耗。 - 永远不要强制解包
urlObj!,用guard let做安全校验,防止URL格式错误导致崩溃。 - 所有UI更新操作必须放在主线程执行,避免界面卡顿或异常。
内容的提问来源于stack exchange,提问作者sugarakis
相关产品推荐
相关产品推荐

