设置新谓词后 NSFetchedResultsController 未正确刷新

Posted

技术标签:

【中文标题】设置新谓词后 NSFetchedResultsController 未正确刷新【英文标题】:NSFetchedResultsController not refreshing properly after setting new predicate 【发布时间】:2015-09-16 21:36:17 【问题描述】:

我的 fetchedResultsController 最初是这样创建的(在 viewDidLoad 中):

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
                               entityForName:@"Cat" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];

NSSortDescriptor *sort = [[NSSortDescriptor alloc]
                          initWithKey:@"age" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];

NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                    managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"age"
                                               cacheName:@"whatever"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;

这很好用,结果显示在我的表格视图中。但是,当按下某些按钮时,我想过滤结果。所以我这样做:

NSPredicate *predicate = ...; //based on buttons pressed


[[self.fetchedResultsController fetchRequest] setPredicate:predicate];
[NSFetchedResultsController deleteCacheWithName:@"whatever"];

NSError * error = nil;
[self.fetchedResultsController performFetch:&error];
if (error) 
    DDLogError(@"[%@ %@] fetchedResultsController ERROR: %@", THIS_FILE, THIS_METHOD, error.localizedDescription);

[self.myTableView reloadData];

问题是表格视图没有重新加载。如果我滚动表格视图,单元格会根据新数据发生变化,但每次尝试过滤时,节数和行数不会发生变化。

我错过了什么?

【问题讨论】:

您应该使用 cmets 中的错误消息将您的问题更新为其他答案。 【参考方案1】:

您需要在对 fetchResultController 进行任何更改之前删除缓存

来自苹果文档:NSFetchedResultsController Class Reference

如果你使用缓存,你必须调用 deleteCacheWithName: 之前 更改任何获取请求、其谓词或排序 描述符。您不得重复使用相同的提取结果控制器 对于多个查询,除非您将 cacheName 设置为 nil。

(可选)控制器应使用的缓存文件的名称 (传递 nil 可防止缓存)。使用缓存可以避免开销 计算节和索引信息。

【讨论】:

感谢您的建议。不幸的是,它并没有改变任何东西。出于某种原因,即使获取的对象的数量确实得到了更新,tableView 也不会重新加载。例如,我将从表中的 4 个部分开始,但过滤后 fetchedResultsController 中将只有 3 个对象,因此如果我尝试滚动表,我将收到 NSRangeException 崩溃。 [__NSArrayM objectAtIndex:]: 索引 3 超出范围 [0 .. 2] 因为您需要更改谓词,所以最好将 cacheName 设置为 nil。没有看到缓存为零使 fecthResultController 保留节号和行号【参考方案2】:

我怀疑您需要根据更新的数据插入和删除表视图行。

您在 cmets 中写给另一个答案的错误消息包含了线索。

相关Apple documentation。

您应该特别阅读的是标题下的文字:行和节的批量插入、删除和重新加载

然而,在此之前,当您更改 NSFetchedResultsController 时,您应该检查您是否实现了 NSFetchedResultsController 委托方法来管理更改。 Apple 提供了标准代码,您应该考虑将其包含在表视图控制器中。您不需要更改/覆盖此标准代码。

看到这个Apple documentation。

对于“典型用例”,您需要实现四种典型的委托方法:

(void)controllerWillChangeContent:(NSFetchedResultsController *)controller; (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo; (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath; (void)controllerDidChangeContent:(NSFetchedResultsController *)controller.

如果您已经包含了这些 NSFetchedResultsController 委托方法,您可能需要考虑将 UITableView 委托方法 beginUpdatesendUpdates 与方法一起使用:

- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation: (UITableViewRowAnimation)animation;
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation: (UITableViewRowAnimation)animation;

所以你应该尝试使用下面的代码块:

tableView.beginUpdates;
tableView.insertRowsAtIndexPaths:indexPaths withRowAnimation:animation;
tableView.deleteRowsAtIndexPaths:indexPaths withRowAnimation:animation;
tableView.endUpdates

您显然需要为 insert 和 delete 方法创建 indexPaths 数组。

【讨论】:

以上是关于设置新谓词后 NSFetchedResultsController 未正确刷新的主要内容,如果未能解决你的问题,请参考以下文章

使用谓词在结果中搜索

具有核心数据关系的 iOS 8 Swift NSFetchResult 谓词无法访问内部 ID

如何设置 NSExtensionActivationRule 谓词?

核心数据和关系谓词

谓词下推

核心数据:实例化后谓词为零