我正在调查一个崩溃的错误,UICollectionView
通过一个通常采用这种形式的通过Crashlytics:
致命异常:NSInternalInconsistencyException无效更新:第0部分中的项目无效.更新后现有部分中包含的项目数(25)必须等于更新前该部分中包含的项目数(27),再加上或减去从该部分插入或删除的项目数量(插入1个,删除1个)以及加入或减去移入或移出该部分的项目数量(0移入,0移出).
我相信这是因为我有一个collectionView,它定期刷新来自服务器的数据,并且来自服务器的数据可能包含比客户端中包含的更多或更少的项目UICollectionViewDataSource
.
当我从服务器获取新数据时,我会调用reloadData
我的集合视图.
但是,有可能由于用户在网络下载完成之前与我的集合视图进行交互,我之前就打过电话reloadItemsAtIndexPaths
.reloadItemsAtIndexPaths
似乎没有完成至少几百毫秒和许多处理器周期.因此,当数据源在中间更新时崩溃reloadItemsAtIndexPaths
.
有"直接"形式reloadItemsAtIndexPaths
吗?或者我必须始终根据reloadData
我的用例进行调用,这似乎会立即更新所有内容并UICollectionView
在最后保持良好状态.
编辑
根据TwoStraws的建议,这就是我所做的:
// Prevent data source from batch updating while we work self.dataSource.locked = YES; [self.collectionView performBatchUpdates:^{ [self.collectionView reloadItemsAtIndexPaths:@[indexPath]]; } completion:^(BOOL finished) { self.dataSource.locked = NO; }];
然后在我的数据源类中,收到服务器的结果后,我总是打电话assignResults
:
- (void)assignResults:(NSMutableArray *)newResults { if (!self.locked) { self.results = newResults; [self.delegate handleDataSourceUpdated:self]; } else { self.pendingResults = newResults; } } - (void)setLocked:(BOOL)locked { _locked = locked; if (!locked && self.pendingResults) { [self assignResults:self.pendingResults]; self.pendingResults = nil; } }
如您所见,如果数据源未锁定,则仅分配结果; 否则,在数据源被解锁时分配它们UICollectionViewController
.请注意,所有这些方法都发生在主线程上,所以我不需要担心我的布尔属性的同步,locked
.
竞争条件总是复杂的问题,我相信你知道.如果我理解正确,你正在修改集合视图的数据源,同时它仍在尝试重新加载自身,这意味着解决方案是保持一个单独的数据存储,并以原子方式复制到集合视图的数据源.
所以:
集合视图从数据源A读取.
网络写入数据源B.
在您指定的时刻,一举将B复制到A.
告诉集合视图重新加载.
这样,集合视图就不必担心竞争条件 - 就它而言,它始终从一组固定的数据中读取.