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

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时还是空的,看起来像没加载到数据。用闭包回调就能解决异步等待的问题。
  • 用扩展做全局函数:如果没加请求控制逻辑,每次调用都会发起新请求,全局函数又容易被多处调用,自然会刷爆服务器。

额外优化建议

  1. 尽量替换全局变量:把ArrayGlobale.listapazienti移到PatientDataManager里作为私有属性,对外提供只读的获取方法,这样更安全,也符合单一职责原则。
  2. 添加本地缓存:可以把解析后的数据存在UserDefaults或CoreData里,下次启动先读本地缓存,再请求最新数据,提升用户体验。
  3. 添加重试机制:请求失败时可以自动重试1-2次,不用用户手动触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:58:36