ViewController间调用JSON解析函数遇请求次数与数据下载问题
解决跨ViewController调用JSON解析函数的问题:重构逻辑+控制请求时机
看起来你现在的核心问题是数据加载时机不对导致的请求泛滥/加载失败,还有跨VC调用业务逻辑的不合理性。咱们一步步来解决:
首先,别再跨VC调用解析函数了!
把网络请求+JSON解析的逻辑从ViewController里抽出来,做成一个独立的单例管理类——这是iOS开发里处理全局数据请求的标准做法,既能避免依赖VC生命周期,又能轻松控制请求次数。
第一步:创建单例数据管理类
这个类专门负责患者数据的请求、解析和缓存,确保只会发起必要的请求:
// 假设你的患者结构体是Patient,要符合Decodable协议 struct Patient: Decodable { // 你的结构体字段,比如id、name等 } // 全局数据存储可以保留,但建议后续逐步移到管理类里 struct ArrayGlobale { static var listapazienti: [Patient] = [] } class PatientDataManager { // 单例:全局唯一实例,确保所有VC用同一个管理对象 static let shared = PatientDataManager() // 标记是否正在请求,防止重复发起请求 private var isFetching = false // 私有初始化方法,禁止外部创建实例 private init() {} // 发起请求的方法,用闭包回调通知结果 func fetchPatients(completion: @escaping (Bool) -> Void) { // 1. 如果正在请求,直接返回,避免重复请求 guard !isFetching else { completion(false) return } // 2. 如果已经有数据了,直接返回成功,不用再请求服务器 guard ArrayGlobale.listapazienti.isEmpty else { completion(true) return } isFetching = true // 3. 替换成你的实际Web Service地址 guard let requestUrl = URL(string: "https://your-api-url.com/patients") else { isFetching = false completion(false) return } // 4. 发起异步网络请求 URLSession.shared.dataTask(with: requestUrl) { [weak self] data, _, error in // 请求结束后重置标记 defer { self?.isFetching = false } // 处理网络错误 guard let data = data, error == nil else { print("网络请求失败:\(error?.localizedDescription ?? "未知错误")") completion(false) return } // 5. JSON解析 do { let patients = try JSONDecoder().decode([Patient].self, from: data) ArrayGlobale.listapazienti = patients completion(true) } catch { print("JSON解析失败:\(error)") completion(false) } }.resume() } }
第二步:在ViewController里正确调用
现在你可以在需要数据的VC里,安全地调用这个方法,不用再纠结ViewDidLoad/ViewDidAppear的问题:
class TargetViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // 发起数据请求,等异步请求完成后再更新UI PatientDataManager.shared.fetchPatients { [weak self] success in // 回到主线程更新UI(网络请求在后台线程) DispatchQueue.main.async { if success { // 数据加载完成,比如刷新表格、初始化UI self?.updateUIWithPatients() } else { // 加载失败,给用户提示 let alert = UIAlertController(title: "提示", message: "加载患者数据失败,请稍后重试", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) self?.present(alert, animated: true) } } } } private func updateUIWithPatients() { // 这里用ArrayGlobale.listapazienti的数据更新你的UI print("加载到\(ArrayGlobale.listapazienti.count)条患者数据") } }
为什么原来的方式会出问题?
- 放在ViewDidAppear里:每次VC显示(比如从其他VC返回)都会触发请求,导致短时间内大量请求打向服务器,甚至阻塞服务。
- 放在ViewDidLoad里:网络请求是异步的,ViewDidLoad执行完时数据还没回来,所以你去访问
ArrayGlobale.listapazienti时还是空的,看起来像没加载到数据。用闭包回调就能解决异步等待的问题。 - 用扩展做全局函数:如果没加请求控制逻辑,每次调用都会发起新请求,全局函数又容易被多处调用,自然会刷爆服务器。
额外优化建议
- 尽量替换全局变量:把
ArrayGlobale.listapazienti移到PatientDataManager里作为私有属性,对外提供只读的获取方法,这样更安全,也符合单一职责原则。 - 添加本地缓存:可以把解析后的数据存在UserDefaults或CoreData里,下次启动先读本地缓存,再请求最新数据,提升用户体验。
- 添加重试机制:请求失败时可以自动重试1-2次,不用用户手动触发。
内容的提问来源于stack exchange,提问作者YouFred
相关产品推荐
相关产品推荐

