调用TableView单元格刷新方法后偶现崩溃,报错行索引越界
UITableView刷新指定单元格崩溃的解决方法
崩溃原因
你遇到的低概率崩溃,本质是竞态条件导致的数据源与操作不匹配:
- 代码里先通过
tableView.indexPath(for: cell)获取了单元格的位置,但这个操作和后续的刷新是异步到主线程执行的 - 在这两个操作的间隙,数据源可能已经发生了变更(比如该单元格对应的数据被删除,section 0的行数变成0)
- 当执行
reloadRows时,TableView发现你要刷新的indexPath(比如row 1)在当前数据源里根本不存在,就会抛出崩溃日志里的错误
解决方案
方案1:放弃通过Cell获取indexPath(推荐)
不要依赖Cell来定位位置,改用数据源的唯一标识(比如模型ID)去查找最新的indexPath,这样能保证操作的是当前数据源中存在的行:
// 假设你的列表模型是YourModel,有唯一标识id func reloadCell(for model: YourModel, in tableView: UITableView) { DispatchQueue.main.async { // 从数据源中查找模型的最新位置 guard let rowIndex = yourDataSourceArray.firstIndex(where: { $0.id == model.id }) else { return } let indexPath = IndexPath(row: rowIndex, section: 0) // 刷新前再次校验indexPath的有效性 guard tableView.numberOfRows(inSection: 0) > rowIndex else { return } tableView.reloadRows(at: [indexPath], with: .automatic) } }
方案2:保留Cell参数,但增加双重校验
如果必须用Cell作为参数,在执行刷新前必须校验indexPath是否还在当前TableView的有效范围内,同时匹配数据源:
func reloadCellOfTableView(_ tableView: UITableView, cell: UITableViewCell) { DispatchQueue.main.async { guard let indexPath = tableView.indexPath(for: cell), // 校验section是否合法 indexPath.section < tableView.numberOfSections, // 校验row是否在当前section的行数范围内 indexPath.row < tableView.numberOfRows(inSection: indexPath.section), // 额外校验数据源是否存在该行(根据你的数据源结构调整) yourDataSourceArray.count > indexPath.row else { return } tableView.reloadRows(at: [indexPath], with: .automatic) } }
补充说明
你之前尝试的tableView.beginUpdates()和tableView.endUpdates()没用,是因为这个崩溃的核心问题是操作的indexPath本身已经无效,和是否批量更新无关,批量更新无法解决无效indexPath的问题。
内容的提问来源于stack exchange,提问作者Tien Ngo Xuan SDC11
相关产品推荐
相关产品推荐

