自定义UITableViewCell数据加载失败 求排查解决方案
嘿,我一眼就看出问题所在了——你把异步网络请求当成同步操作来处理了,这才导致你的recipes数组一直是空的,表格自然加载不出数据。咱们一步步拆解问题,再给你修复方案:
核心问题拆解
异步请求的同步返回陷阱
你的getRecipes()方法里,Moya的provider.request是异步执行的——也就是说,当你return recipesTemp的时候,网络请求还在后台跑,recipesTemp根本还没被API返回的数据填充,所以返回的是空数组。这就导致recipes = self.getRecipes()拿到的永远是空值。刷新时机与代码顺序错误
你在viewDidLoad里先调用了recipeTableView.reloadData(),这时候recipes还是空的,完全没必要;而且你把super.viewDidLoad()放在了最后,这不符合UIViewController的生命周期规范,可能引发未知问题。另外就算后来网络请求完成后你调用了reloadData,但之前的recipes赋值逻辑已经错了,数据根本没同步到全局的recipes数组里。方法位置错误
你把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

