iOS UICollectionView批量更新崩溃求助(NSInternalInconsistencyException)
UICollectionView批量更新偶现崩溃排查
问题背景
网络请求后尝试更新仅含1个section的UICollectionView,插入新元素,偶现崩溃。代码参考过Raywenderlich论坛及StackOverflow方案,但无法稳定复现问题。
核心更新代码
func process(_ difference: [CollectionDifference<User>.Change]?) { guard let difference = difference, !difference.isEmpty else { updateDataSource() collectionView.reloadData() return } let deleting = difference.contains { change in if case let .remove(offset: _, element: _, associatedWith: associatedWith) = change { return associatedWith == nil } return false } let inserting = difference.contains { change in if case let .insert(offset: _, element: _, associatedWith: associatedWith) = change { return associatedWith == nil } return false } isCollectionViewUpdating = true collectionView.performBatchUpdates({ updateDataSource() for change in difference { switch change { case let .insert(offset: offset, element: recommendation, associatedWith: _): collectionView.insertItems(at: [IndexPath(row: offset, section: 0)]) case let .remove(offset: offset, element: _, associatedWith: _): collectionView.deleteItems(at: [IndexPath(row: offset, section: 0)]) } } }, completion: { [weak self] _ in guard let self = self else { return } self.isCollectionViewUpdating = false self.finalizingIndexPaths.removeAll() if deleting { self.paginate() } else if inserting { self.delegate?.didInsertInCollectionView() } }) }
崩溃错误日志
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid batch updates detected: the number of sections and/or items returned by the data source before and/or after performing the batch updates are inconsistent with the updates. Data source before updates = { 1 section with item counts: [16] } Data source after updates = { 1 section with item counts: [16] } Updates = [ Insert item (0 - 0), Insert item (0 - 1), Insert item (0 - 2), Insert item (0 - 3), Insert item (0 - 4), Insert item (0 - 5), Insert item (0 - 6), Insert item (0 - 7), Insert item (0 - 8), Insert item (0 - 9), Insert item (0 - 10), Insert item (0 - 11), Insert item (0 - 12), Insert item (0 - 13), Insert item (0 - 14), Insert item (0 - 15) ]
相关生命周期回调代码
该UICollectionView所在视图控制器作为子控制器嵌入标签栏控制器的父控制器中,包含以下回调:
public func willBecomeActive() { view.layoutIfNeeded() collectionView.reloadData() // need to add this empty performBatchUpdates, otherwise the cells don't // come on screen in tests collectionView.performBatchUpdates({}, completion: nil) } public func didResignActive() { collectionView.reloadData() } override public func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() collectionViewFlowLayout.updateItemSize() // helps to correctly resizes the cells. UIView.performWithoutAnimation { collectionView.performBatchUpdates({}, completion: nil) } }
问题分析与修复建议
1. 并发更新冲突
当前代码中isCollectionViewUpdating标记仅在批量更新时设置,但生命周期回调中的reloadData()和空performBatchUpdates()未判断该标记,可能在批量更新过程中触发额外的数据源刷新,导致系统校验数据源状态时出现不一致。
修复:在所有操作UICollectionView的生命周期方法中增加状态判断:
public func willBecomeActive() { view.layoutIfNeeded() guard !isCollectionViewUpdating else { return } collectionView.reloadData() collectionView.performBatchUpdates({}, completion: nil) } public func didResignActive() { guard !isCollectionViewUpdating else { return } collectionView.reloadData() } override public func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() collectionViewFlowLayout.updateItemSize() guard !isCollectionViewUpdating else { return } UIView.performWithoutAnimation { collectionView.performBatchUpdates({}, completion: nil) } }
2. 数据源更新与批量操作的逻辑不匹配
错误日志显示:更新前后数据源数量均为16,但执行了16个insert操作,说明updateDataSource()执行后数据源未发生变化,与声明的insert操作矛盾。
可能原因及修复:
- 检查
updateDataSource()逻辑:若该方法是直接替换整个数据源而非根据difference增量更新,会导致difference中的offset与实际数据源变化不匹配。需确保updateDataSource()是根据传入的difference对旧数据源进行增量修改(插入/删除对应位置的元素)。 - 检查
CollectionDifference的生成逻辑:确认差异计算是基于旧数据源和新数据源的正确对比,且User类型的Equatable实现正确(避免误判元素重复/缺失)。
3. 批量更新的执行顺序
performBatchUpdates闭包中先调用updateDataSource()再执行insert/delete操作,虽符合常规流程,但需确保:
- 闭包执行前,数据源处于旧状态;
- 闭包执行后,数据源处于新状态;
- 声明的insert/delete操作数量与新旧数据源的数量差完全一致(旧数量 + 插入数 - 删除数 = 新数量)。
若difference中包含无效的insert操作(比如元素已存在于数据源),需在处理前过滤掉这类操作,或重新计算正确的差异。
内容的提问来源于stack exchange,提问作者DesperateLearner
相关产品推荐
相关产品推荐

