今天coding的时候出现了如题那个bug,在网上找了找没有找到具体的解决方法,不过大神们也给出了了问题原因方向:
- 问题一般出现在操作cell或者section的时候,例如调用下例方法:
- (void)insertItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
- (void)deleteItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
- (void)reloadItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
- 导致问题的原因应该是数据源的数量与操作cell后的cell的数量不一致,或者是由于
- (NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section
这个方法中返回值写死了导致的。
分析(可以忽略直接看最后)
- 通过我自己测试发现,我在调用
reloadData
后,在很短的时间内又调用了insertItemsAtIndexPaths
方法才导致崩溃的发生,但是通过打印发现在调用reloadData
后,系统并没有紧接着调用numberOfItemsInSection
数据源方法,所以可以得知collectionView
调用数据源方法是异步的。 - 假设现在请求下来10个数据,然后调用
reloadData
,然后在很短的时间内调用
[self.userInfos insertObject:info atIndex:0];
[self.collectionView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:0]]];
由于数据源方法是异步的,在此之前还没有调用数据源方法numberOfItemsInSection
,所以此时collectionView
并不知道自身有多少个cell(或者说collectionView
的cell还没有创建出来),也就无法进行cell的添加删除和刷新,所以会提示InvalidationContext
。
- 另外,在
reloadData
方法说明中,苹果已经给出提示:
You should not call this method in the middle of animation blocks where items are being inserted or deleted. Insertions and deletions automatically cause the table’s data to be updated appropriately.
不可以在插入和删除cell的时候调用reloadData
方法。
解决方法
延时调用:insertItemsAtIndexPaths
方法,也就是在collectionView
调用完数据源方法后再进行cell的操作。一定要这样写(时间自己设定)
或者让collectionView
主动调用一次据源方法,知道自己有多少个cell后才能操作cell,这样写:
(2016.9.21更新)
由于此问题出现的概率不大,所以对于测试问题解决方式来说有很多不确定性。之前的方法我本以为彻底解决了问题,没想到上线后同样的问题又出现了,很是无奈,只好另外再找解决方法,使用下面方法到现在基本上没有再出崩溃的问题,大家可以参考测试:
- 删除时
[self.infos removeLastObject];
if ([self.collectionView numberOfItemsInSection:0] == self.infos.count) {
[self.collectionView reloadData];
}else{
[self.collectionView deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:self.infos.count-1 inSection:0]]];
}
- 添加时
[self.infos insertObject:info atIndex:0];
if (self.infos.count == 1 || [self.collectionView numberOfItemsInSection:0] == self.infos.count) {
[self.collectionView reloadData];
}else{
[self.collectionView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:0]]];
}
说明
- 就是在调用
insertItemsAtIndexPaths
和deleteItemsAtIndexPaths
之前要判断下当前cell数量是否和model数量相同,如果相同的话则不应该再插入或者删除cell - 插入cell时如果插入的是第一个(插入之前cell数量为0)时也不要调用
insertItemsAtIndexPaths
,而是调用reloadData