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

自定义UITableViewCell数据加载失败 求排查解决方案

问题分析与修复:UITableView无法加载异步API返回的数据

嘿,我一眼就看出问题所在了——你把异步网络请求当成同步操作来处理了,这才导致你的recipes数组一直是空的,表格自然加载不出数据。咱们一步步拆解问题,再给你修复方案:

核心问题拆解

  1. 异步请求的同步返回陷阱
    你的getRecipes()方法里,Moya的provider.request是异步执行的——也就是说,当你return recipesTemp的时候,网络请求还在后台跑,recipesTemp根本还没被API返回的数据填充,所以返回的是空数组。这就导致recipes = self.getRecipes()拿到的永远是空值。

  2. 刷新时机与代码顺序错误
    你在viewDidLoad里先调用了recipeTableView.reloadData(),这时候recipes还是空的,完全没必要;而且你把super.viewDidLoad()放在了最后,这不符合UIViewController的生命周期规范,可能引发未知问题。另外就算后来网络请求完成后你调用了reloadData,但之前的recipes赋值逻辑已经错了,数据根本没同步到全局的recipes数组里。

  3. 方法位置错误
    你把viewDidAppear写在了UITableViewDataSource的extension里,这是错误的——viewDidAppear是UIViewController的方法,应该放在主类中。

修复方案

咱们把异步请求的逻辑改对,让数据正确更新后再刷新表格:

第一步:修改getRecipes方法,用闭包传递异步结果

把原来的返回值去掉,换成闭包来传递获取到的菜谱数组(或者错误信息),这样才能拿到异步请求的结果:

func getRecipes(completion: @escaping ([Recipe]?, Error?) -> Void) {
    let provider = MoyaProvider<SpoonacularAPI>()
    provider.request(.getRecipes(limitLicense: true, number: 10, tags: "vegetarian, dessert")) { result in
        switch result {
        case .success(let response):
            do {
                _ = try response.filterSuccessfulStatusCodes()
                let json = try JSON(data: response.data)
                let recipesTemp = json["recipes"].arrayValue.map({ Recipe(json: $0) })
                completion(recipesTemp, nil)
            } catch {
                completion(nil, error)
            }
        case .failure(let error):
            completion(nil, error)
        }
    }
}

第二步:修正viewDidLoad的逻辑与顺序

调整代码顺序,在正确的时机调用异步方法,拿到数据后更新全局数组并刷新表格:

override func viewDidLoad() {
    super.viewDidLoad() // 必须放在最前面,遵循生命周期规范
    recipeTableView.delegate = self
    recipeTableView.dataSource = self
    
    // 异步获取菜谱数据
    getRecipes { [weak self] recipes, error in
        guard let self = self else { return }
        if let recipes = recipes {
            self.recipes = recipes
            print("array: \(self.recipes.count)")
            self.recipeTableView.reloadData()
        } else if let error = error {
            print("获取菜谱失败:\(error.localizedDescription)")
        }
    }
}

第三步:修正其他小问题

  • 把viewDidAppear移到主类中,除非有特殊场景需要强制刷新,否则不需要额外调用reloadData
  • 确保你的Recipe模型的init(json: JSON)方法能正确解析JSON数据,这也是数据加载失败的潜在原因

修改后的完整代码

class MainPageViewController: UIViewController { 
    //declare variables 
    @IBOutlet weak var recipeTableView: UITableView! 
    var recipes: [Recipe] = [] 
    
    override func viewDidLoad() { 
        super.viewDidLoad() // 修正顺序,先调用super方法
        recipeTableView.delegate = self 
        recipeTableView.dataSource = self 
        
        // 异步获取菜谱数据
        getRecipes { [weak self] recipes, error in
            guard let self = self else { return }
            if let recipes = recipes {
                self.recipes = recipes
                print("array: \(self.recipes.count)")
                self.recipeTableView.reloadData()
            } else if let error = error {
                print("Error fetching recipes: \(error.localizedDescription)")
            }
        }
    } 
    
    override func viewDidAppear(_ animated: Bool) { 
        super.viewDidAppear(animated)
        // 除非有需要在页面显示时强制刷新的场景,否则这里可以去掉reloadData
        // recipeTableView.reloadData()
    }
} 

// UITableView相关扩展
extension MainPageViewController: UITableViewDataSource, UITableViewDelegate { 
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
        print("array count: \(recipes.count)") 
        return recipes.count 
    } 
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
        print("code here") 
        let recipe = self.recipes[indexPath.row] 
        let cell = tableView.dequeueReusableCell(withIdentifier: "RecipeCell") as! UIRecipeCell 
        print("code here") 
        cell.setRecipe(recipe: recipe) 
        return cell
    } 
    
    func getRecipes(completion: @escaping ([Recipe]?, Error?) -> Void) { 
        let provider = MoyaProvider<SpoonacularAPI>() 
        provider.request(.getRecipes(limitLicense: true, number: 10, tags: "vegetarian, dessert")) { result in
            switch result { 
            case .success(let response): 
                do { 
                    _ = try response.filterSuccessfulStatusCodes() 
                    let json = try JSON(data: response.data) 
                    print("json log: \(json)") 
                    let recipesTemp = json["recipes"].arrayValue.map({ Recipe(json: $0) }) 
                    print("array count: \(recipesTemp.count)") 
                    print("array : \(recipesTemp)") 
                    completion(recipesTemp, nil)
                } catch { 
                    print(error.localizedDescription) 
                    completion(nil, error)
                } 
            case .failure(let error): 
                print(error.localizedDescription) 
                completion(nil, error)
            } 
        } 
    } 
}

额外注意点

  • 用[weak self]避免循环引用,防止内存泄漏
  • 可以考虑添加加载状态(比如UIActivityIndicatorView),让用户知道正在获取数据
  • 确保UIRecipeCell的setRecipe方法能正确把数据赋值到对应的UI组件上(比如UILabel、UIImageView等)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:02:00