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

Swift中移parseJSON至NetworkManager后TableView无数据问题排查

问题解析:将JSON解析移至NetworkManager后UITableView无法显示数据

问题描述

我花了好几天找解决方案,想把ViewController里的parseJSON()方法移出去优化代码结构,但移到NetworkManager之后,尽管数据能正常解析,UITableView就是显示不了数据。原来在ViewController里的代码能正常显示解析后的表格数据,试过加reloadData()也没用。

可正常工作的代码

class MainViewController: UIViewController {

private var companyModel: CompanyModel? 
private lazy var employeesTable: UITableView = { 
    let table = UITableView(frame: .zero, style: .grouped)
    table.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
    return table
}()

override func viewDidLoad() {
    super.viewDidLoad()
    parseJSON()
    view.addSubview(employeesTable)
    employeesTable.frame = view.bounds
    employeesTable.delegate = self
    employeesTable.dataSource = self
}

private func parseJSON() {
    guard let url = URL(string: "https://run.mocky.io/v3/1d1cb4ec-73db-4762-8c4b-0b8aa3cecd4c") else { return }
    do {
        let jsonData = try Data(contentsOf: url)
        companyModel = try JSONDecoder().decode(CompanyModel.self, from: jsonData)
    } catch {
        print(error)
    }
}

移至NetworkManager后失效的代码

import Foundation

class NetworkManager {
    
    var companyModel: CompanyModel?
    func parseJSON() {
        guard let url = URL(string: "https://run.mocky.io/v3/1d1cb4ec-73db-4762-8c4b-0b8aa3cecd4c") else { return }
        do {
            let jsonData = try Data(contentsOf: url)
            companyModel = try JSONDecoder().decode(CompanyModel.self, from: jsonData)
        } catch {
            print(error)
        }
    }
}

import UIKit

class MainViewController: UIViewController {
    
    var networkManager = NetworkManager()
    private var companyModel: CompanyModel?
    private lazy var employeesTable: UITableView = {
        let table = UITableView(frame: .zero, style: .grouped)
        table.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        return table
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        networkManager.parseJSON()
        view.addSubview(employeesTable)
        employeesTable.frame = view.bounds
        employeesTable.delegate = self
        employeesTable.dataSource = self
    }
}

问题原因

  1. 数据未同步到ViewController的数据源
    NetworkManager内部维护了自己的companyModel实例,但ViewController的companyModel依然是空的——而TableView的数据源绑定的是ViewController的companyModel,自然没有数据可以显示。

  2. 同步网络请求的隐患
    你用的Data(contentsOf:)是同步网络请求,会阻塞主线程,这本身就是不规范的开发写法。虽然当前代码中同步请求会立即执行,但实际场景下异步请求才是正确选择,而异步请求会导致你调用parseJSON()后,数据还没解析完成就已经初始化了TableView,后续也没有触发刷新。

修复方案

推荐方案:用闭包回调实现数据传递(解耦+异步)

修改NetworkManager,让它通过闭包返回解析结果,同时改用异步URLSession请求:

class NetworkManager {
    func fetchCompanyData(completion: @escaping (CompanyModel?, Error?) -> Void) {
        guard let url = URL(string: "https://run.mocky.io/v3/1d1cb4ec-73db-4762-8c4b-0b8aa3cecd4c") else {
            let error = NSError(domain: "NetworkError", code: -1, userInfo: [NSLocalizedDescriptionKey: "无效的请求URL"])
            completion(nil, error)
            return
        }
        
        URLSession.shared.dataTask(with: url) { data, _, error in
            if let error = error {
                completion(nil, error)
                return
            }
            
            guard let data = data else {
                let error = NSError(domain: "NetworkError", code: -2, userInfo: [NSLocalizedDescriptionKey: "未获取到数据"])
                completion(nil, error)
                return
            }
            
            do {
                let companyModel = try JSONDecoder().decode(CompanyModel.self, from: data)
                completion(companyModel, nil)
            } catch {
                completion(nil, error)
            }
        }.resume()
    }
}

然后修改ViewController的调用逻辑,拿到数据后更新模型并刷新TableView:

override func viewDidLoad() {
    super.viewDidLoad()
    // 先配置TableView
    view.addSubview(employeesTable)
    employeesTable.frame = view.bounds
    employeesTable.delegate = self
    employeesTable.dataSource = self
    
    // 请求数据并更新UI
    networkManager.fetchCompanyData { [weak self] model, error in
        guard let self = self else { return }
        
        if let error = error {
            print("数据请求失败:\(error)")
            return
        }
        
        // 更新ViewController的模型
        self.companyModel = model
        // 回到主线程刷新TableView(网络请求在后台线程)
        DispatchQueue.main.async {
            self.employeesTable.reloadData()
        }
    }
}

备选方案(不推荐,耦合性高)

如果不想修改闭包,可以直接复用NetworkManager的模型,但这种方式会让ViewController和NetworkManager强耦合,不利于后续维护:

override func viewDidLoad() {
    super.viewDidLoad()
    networkManager.parseJSON()
    // 直接赋值NetworkManager的模型
    self.companyModel = networkManager.companyModel
    view.addSubview(employeesTable)
    employeesTable.frame = view.bounds
    employeesTable.delegate = self
    employeesTable.dataSource = self
    // 手动刷新表格
    employeesTable.reloadData()
}

注意:这种方式仅适用于同步请求,异步请求下数据还没解析完成,companyModel依然是空的,所以不推荐。

额外检查点

确保你的UITableView数据源方法正确使用了companyModel的数据,例如:

extension MainViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return companyModel?.employees.count ?? 0
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = companyModel?.employees[indexPath.row].name
        return cell
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 23:00:54