我正在通过Crashlytics调查一个UICollectionView
崩溃的bug,它通常采用以下形式:
致命异常: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
的同步。
答案 0 :(得分:8)
竞赛条件总是复杂的问题,因为我相信你知道。如果我理解正确,你正在修改集合视图的数据源,而它仍在尝试重新加载自己,这意味着解决方案是保留一个单独的数据存储,并以原子方式复制到集合视图的数据源。 / p>
所以:
这样,集合视图就不必担心竞争条件 - 它总是从一组固定的数据中读取。就其而言。