UITableView调用reloadData()旧单元格未清除,新单元格追加问题求助
解决UITableView reloadData后旧单元格残留的问题
我帮你排查出了几个导致这个问题的关键原因,咱们一个个来修正:
1. JSON解析的致命错误
你的代码里有两个明显的错误导致新数据根本没被正确加载到数据源数组:
- 变量名写错了:你把解析后的JSON结果存在
jsonResult里,但循环遍历的时候用的是未定义的jsonResponse,这直接导致循环代码根本不会执行,sourceArray自然还是旧数据! - 类型不匹配:
sourceArray是[String]类型,但你直接把字典(thing是Dictionary<String, Any>)添加进去,这会导致编译错误,就算强行运行也无法正确显示内容。你需要从字典里取出对应的字符串值再添加到数组。
2. 单元格复用逻辑错误
你每次都创建新的UITableViewCell,没有利用tableView的复用池机制。这不仅会降低性能,还可能导致旧单元格无法被正确回收和替换。
3. 数据源更新的线程安全
虽然你在主线程调用了reloadData(),但修改sourceArray的操作是在后台线程完成的,这可能引发竞态条件,导致tableView读取到不一致的数据源状态。
修正后的完整代码
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { var sourceArray = [String]() var otherClassVariable: String? // 直接改成String类型,避免Any?的类型转换麻烦 // 先在viewDidLoad里注册单元格复用标识符 override func viewDidLoad() { super.viewDidLoad() // 注册复用单元格,这样才能正确dequeue tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell") getNewDataAndUpdateTable() } public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return sourceArray.count } public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // 使用复用机制获取单元格,而不是每次新建 let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) cell.textLabel?.text = sourceArray[indexPath.row] return cell } func getNewDataAndUpdateTable() { // 先清空数据源,注意最好在主线程操作数组 DispatchQueue.main.async { self.sourceArray.removeAll() self.tableView.reloadData() // 先清空表格,避免等待网络请求时显示旧数据 } guard let param = otherClassVariable, let url = URL(string: "https://myurl.com/\(param)") else { print("参数或URL无效") return } let task = URLSession.shared.dataTask(with: url) { (data, response, error) in if let error = error { print("请求错误:\(error.localizedDescription)") return } guard let urlContent = data else { print("未获取到数据") return } do { // 解析JSON,这里假设你需要从字典里取"title"字段作为字符串,根据你的实际JSON结构修改 let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: []) as! [[String: Any]] var newData = [String]() for thing in jsonResult { // 替换成你JSON里实际的字符串字段名,比如"name"、"content"等 if let text = thing["title"] as? String { newData.append(text) } } // 回到主线程更新数据源和表格 DispatchQueue.main.async { self.sourceArray = newData self.tableView.reloadData() } } catch { print("JSON解析失败:\(error.localizedDescription)") } } task.resume() } @IBAction func unwindToViewController(_ sender: UIStoryboardSegue) { guard let senderVC = sender.source as? SecondViewController else { return } self.otherClassVariable = senderVC.updatedOtherClassVariable getNewDataAndUpdateTable() } }
关键修改点说明
- 把
otherClassVariable改成String?类型,避免不必要的类型转换和强制解包风险 - 在
viewDidLoad里注册单元格复用标识符,确保能正确复用单元格 - 修复了JSON解析的变量名错误,并且添加了类型安全的字段提取
- 将数据源的修改操作全部放到主线程,避免线程安全问题
- 增加了空值判断,让代码更健壮
- 在发起网络请求前先清空数据源并刷新表格,避免等待期间显示旧数据
内容的提问来源于stack exchange,提问作者BenJ
相关产品推荐
相关产品推荐

