我们的应用已UICollectionView
,其dataSource
字典会定期更新。我们永远不知道下次更新何时会发生。可以在用户点击按钮后调用Collection View重载方法,也可以在网络请求成功后异步进行。鉴于上述信息,我们可能会在重新加载集合视图和同时更新其数据源时遇到竞争条件。我们甚至注册了下面的崩溃,我们认为它是因为上述竞争条件而发生的。崩溃消息:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
导致崩溃的方法:collectionViewLayout sizeForItemAtIndexPath:
。
此方法根据sectionWithProducts
中的项目计数计算集合视图的高度。它崩溃是因为dataSource
计数小于indexPath.row
。导致崩溃的线路:
NSArray *sectionWithProducts = self.dataSource[indexPath.row];
在发生崩溃之前调用以下行:
[self.collectionView setCollectionViewLayout:[self flowLayout] animated:NO];
[self.collectionView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
[self.collectionView reloadData];
为了防止这种情况,我们决定将唯一一行更新数据源的代码放入主线程中。
// Always run on main thread in hope to prevent NSRangeException.
// Reloading data should happen only sequential.
dispatch_async(dispatch_get_main_queue(), ^(void) {
self.dataSource = newValue;
});
我们的代码中有很多[self.collectionView reloadData]
。是否值得在主线程上运行它们?它发生得很快,所以它不应该长时间阻止UI。
UICollectionViewDelegateFlowLayout
委托方法是否始终在后台队列中调用indexPath
属性?
答案 0 :(得分:0)
首先,应该在主线程上调用所有UIKIT方法,包括reloadData。这虽然不能解决你的崩溃。其次,在代码中的某个位置存在竞争条件,您正在调用reloadData并同时更改数据源,您需要确定它发生的位置。这是我可以说的,没有看到实际的代码。