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:^{ 这里处理 }]; });
愿好运