今天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