UITableView重载时更新数据源致崩溃,如何安全使用局部刷新方法?
异步更新多分组UITableView:安全使用精细刷新方法的方案
当然可以用reloadRows(at:with:)、insertRows(at:with:)这类精细刷新方法,核心是解决数据源并发修改和刷新操作线程安全的问题,以下是具体实现思路:
1. 强制所有UI相关操作在主线程执行
网络请求回调默认在后台线程,所有修改数据源后调用UITableView刷新方法的逻辑,必须切换到主线程执行:
DispatchQueue.main.async { // 先修改数据源模型 self.sections[0].rows[1] = updatedRowModel // 再调用精细刷新 self.tableView.reloadRows(at: [IndexPath(row: 1, section: 0)], with: .automatic) }
2. 加锁保护数据源的读写操作
异步回调可能同时触发,多个线程并发修改数据源会导致数据不一致,甚至刷新时数据源状态异常。用NSLock或串行队列做同步:
private let dataLock = NSLock() private var tableSections: [SectionModel] = [] // 修改数据源时加锁 func updateSection(at index: Int, with newData: SectionModel) { dataLock.lock() defer { dataLock.unlock() } guard tableSections.count > index else { return } tableSections[index] = newData } // UITableView数据源方法中读取数据也要加锁 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { dataLock.lock() defer { dataLock.unlock() } return tableSections[section].rowCount }
3. 用批量更新包裹多操作
如果一次回调需要执行多个增删改操作,用beginUpdates()和endUpdates()包裹,避免多次刷新引发的冲突:
DispatchQueue.main.async { self.dataLock.lock() // 批量修改数据源:插入一行 + 修改一行 let insertIndex = IndexPath(row: 2, section: 1) self.tableSections[1].rows.insert(newRow, at: insertIndex.row) let updateIndex = IndexPath(row: 0, section: 0) self.tableSections[0].rows[updateIndex.row] = updatedRow self.dataLock.unlock() self.tableView.beginUpdates() self.tableView.insertRows(at: [insertIndex], with: .fade) self.tableView.reloadRows(at: [updateIndex], with: .automatic) self.tableView.endUpdates() }
4. 避免刷新过程中触发新的刷新
添加状态标记位,防止刷新未完成时再次触发刷新,同时可缓存待处理的更新操作:
private var isRefreshing = false private var pendingUpdates: [UpdateOperation] = [] func processTableUpdate(_ update: UpdateOperation) { DispatchQueue.main.async { guard !self.isRefreshing else { self.pendingUpdates.append(update) return } self.isRefreshing = true // 执行数据源修改和刷新逻辑 self.dataLock.lock() // ... 处理当前update对应的数据源修改 self.dataLock.unlock() self.tableView.beginUpdates() // ... 执行对应的增删改刷新 self.tableView.endUpdates() self.isRefreshing = false // 处理缓存的待更新操作 if !self.pendingUpdates.isEmpty { let pending = self.pendingUpdates self.pendingUpdates.removeAll() pending.forEach { self.processTableUpdate($0) } } } }
5. 进阶方案:使用DiffableDataSource(iOS 13+)
苹果官方的UITableViewDiffableDataSource内置了差异计算和线程安全处理,完美适配异步更新场景:
// 定义分组和行数据的可哈希类型 enum TableSection: Hashable { case main, secondary } struct RowModel: Hashable { let id: String let content: String } // 初始化DiffableDataSource private var diffableDataSource: UITableViewDiffableDataSource<TableSection, RowModel>! func setupTableView() { diffableDataSource = UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, model in let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) cell.textLabel?.text = model.content return cell } } // 异步更新时直接生成新快照并应用 func updateTable(with newRows: [RowModel], for section: TableSection) { DispatchQueue.main.async { var snapshot = self.diffableDataSource.snapshot() if !snapshot.sectionIdentifiers.contains(section) { snapshot.appendSections([section]) } snapshot.appendItems(newRows, toSection: section) // apply方法自动处理刷新冲突和动画 self.diffableDataSource.apply(snapshot, animatingDifferences: true) } }
只要严格遵循以上规则,就能安全使用精细刷新方法,无需依赖全局的reloadData。
内容的提问来源于stack exchange,提问作者zzheads
相关产品推荐
相关产品推荐

