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

UITableView刷新.stock分区报错:尝试删除分区1但更新前仅1个分区

问题:UITableView仅刷新指定分区时出现删除分区报错

需求为仅刷新.stock分区(.filter分区内容固定),因每秒向API发起请求,使用tableView.reload()会导致Filter单元格的UIMenu关闭,故选择刷新指定分区,但操作时触发报错:

Thread 1: "attempt to delete section 1, but there are only 1 sections before the update"


相关代码片段

TableView分区数量实现

func numberOfSections(in tableView: UITableView) -> Int {
    guard let sectionTypesCount = viewModel?.sectionTypes.count
    else { return 0 }
    return sectionTypesCount
}

TableView分区行数实现

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    guard let sectionType = viewModel?.sectionTypes[section]
    else { return 0 }
    
    switch sectionType {
    case .filter:
        return 1
    case .stock(let myPageDefaults):
        return myPageDefaults.count
    }
}

分区刷新代码(报错触发位置)

if let index = self.viewModel?.sectionTypes.firstIndex(where: { if case .stock = $0 { return true }; return false }) {
    let section = [index]
    let indexSet = IndexSet(section)
    self.tableView.reloadSections(indexSet, with: .fade)
    self.viewModel?.refreshService2EverySecond()
} else {
    self.tableView.reloadData()
    self.viewModel?.refreshService2EverySecond()
}

.stock分区数据赋值逻辑

if let index = self.sectionTypes.firstIndex(where: { if case .stock = $0 { return true }; return false }) {
    self.sectionTypes[index] = .stock(l.map({self.makeRowForMyPageDefault(with: $0)}))
} else {
    self.sectionTypes.append(.stock(l.map({self.makeRowForMyPageDefault(with: $0)})))
}

问题原因及解决办法

报错核心是刷新操作时,UIKit检测到数据源分区数量与刷新前不一致,大概率是数据源更新和UI刷新时机未对齐,或sectionTypes数组在刷新时发生意外变更。

具体修复步骤

  1. 强制数据源更新与UI刷新在主线程执行
    UITableView刷新必须在主线程完成,若API请求在后台线程,需将数据赋值逻辑切换至主线程,并在数据源更新完成后再触发刷新:

    DispatchQueue.main.async {
        if let index = self.sectionTypes.firstIndex(where: { if case .stock = $0 { return true }; return false }) {
            self.sectionTypes[index] = .stock(l.map({self.makeRowForMyPageDefault(with: $0)}))
        } else {
            self.sectionTypes.append(.stock(l.map({self.makeRowForMyPageDefault(with: $0)})))
        }
        self.refreshStockSection()
    }
    
  2. 刷新前校验分区索引有效性
    修改刷新逻辑,先确认.stock分区索引在数据源的有效范围内,避免无效索引触发错误:

    func refreshStockSection() {
        guard let viewModel = self.viewModel,
              let index = viewModel.sectionTypes.firstIndex(where: { if case .stock = $0 { return true }; return false }),
              index < viewModel.sectionTypes.count
        else {
            self.tableView.reloadData()
            viewModel.refreshService2EverySecond()
            return
        }
        let indexSet = IndexSet(integer: index)
        self.tableView.reloadSections(indexSet, with: .fade)
        viewModel.refreshService2EverySecond()
    }
    
  3. 防止并发刷新导致数据源混乱
    每秒请求API易造成刷新叠加,可添加标志位限制并发操作:

    private var isRefreshing = false
    
    func refreshService2EverySecond() {
        guard !isRefreshing else { return }
        isRefreshing = true
        // 发起API请求
        apiRequest { [weak self] result in
            self?.isRefreshing = false
            // 处理数据更新和刷新
        }
    }
    

内容的提问来源于stack exchange,提问作者Ufuk Köşker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 21:20:26