2015-04-30 139 views
5

我们的应用有UICollectionView和它的dataSource字典定期更新。我们永远不知道何时会发生下一次更新。集合视图重载方法可以在用户点击按钮后调用,也可以在网络请求成功后发生异步。鉴于上述信息,我们有可能在重新加载收集视图并同时更新其数据源时遇到竞争状况。我们甚至注册了下面的崩溃,并且我们相信它是因为上述竞争条件而发生的。崩溃消息:防止在UICollectionView中崩溃

*** 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

首先,应在主线程上调用所有的UIKIT方法,包括reloadData。这虽然不会解决你的崩溃。其次,在你的代码中有一个竞争条件,你要调用reloadData并同时改变数据源,你需要找出它发生的地方。这是我可以说不看实际的代码。