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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 22:45:28