NSFetchedResultsController
是一個很是好用且強大的數據庫綁定類,用來處理CoreData
和UIView
的數據綁定很是便捷。前端
例如官方例子中,實用NSFetchedResultsController
綁定UITableView
,完成綁定後,開發者只要專一處理數據就好,UI會根據數據變化自動更新。數據庫
而且NSFetchedResultsController
還提供了緩存功能,大大提升了大數據量的CoreData
檢索效率。數組
然而這麼好的東西,和UICollectionView
並不能配合的很是默契。緩存
兩個緣由:async
UICollectionView
再也不有UITableView
的-(void)beginUpdates
和`-(void)endUpdates`的方法,但卻提供了一個使人尷尬的批量處理Cell的外包圍方法大數據
- (void)performBatchUpdates:(void (^ __nullable)(void))updates completion:(void (^ __nullable)(BOOL finished))completion;
這個方法把操做包含到了block中執行,可是NSFetchedResultsController
的UI改變通知回調是分四個回調方法完成的。因此我被迫使用了NSBlockOperation
,把中間回調進行的操做包裝到block中,再存到數組中,統一在atom
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
中執行。但在當前頁面刪除cell時候仍是會報錯。尷尬的我只好去掉了performBatchUpdates
外包圍,直接執行block,此次看起來沒有問題了。spa
包裝block的方法是這樣線程
@property (nonatomic, strong) NSMutableArray* op; [self.op addObject:[NSBlockOperation blockOperationWithBlock:^{ [collectionView insertItemsAtIndexPaths:@[newIndexPath]]; }]];
執行block的方法是這樣code
[self.op enumerateObjectsUsingBlock:^(NSBlockOperation* _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [[NSOperationQueue mainQueue] addOperation:obj]; }];
第二個坑,是UICollectionView,並不能夠在幕後之行insert
和delete
操做,會拋一個莫名其秒的異常。所謂幕後,就是UICollectionView的VC不是當前顯示在最前端的VC,這種狀況也是很常見的。
因此這個時候,要直接調用[collectionView reloadData]
方法。
像這樣
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { if (self.onTop) { [self.op enumerateObjectsUsingBlock:^(NSBlockOperation* _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [[NSOperationQueue mainQueue] addOperation:obj]; }]; } else { [self.collectionView reloadData]; } }
這就是performBatchUpdates
方法存在的意義。
因此仍是須要用performBatchUpdates
block住批量修改UI的代碼,才能不報錯。
而這個方法,必須在主線程執行。
dispatch_async(dispatch_get_main_queue(), ^{ [self.collectionView performBatchUpdates:^{ 這裏處理 }]; });
願好運