【问题标题】:How can I refresh a NSFetchedResultsController?如何刷新 NSFetchedResultsController?
【发布时间】:2023-03-15 04:53:01
【问题描述】:

我有一个 NSFetchedResultsController,它在 UITableView 中显示数据。在选择创建 Core Data 项目时,我使用的是 Xcode 提供的样板代码。我在 NSFetchedResultsController 使用的 NSFetchRequest 中添加了以下谓词(在 NSFetchedResultsController 初始化之前):

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"deleted == NO"];
[fetchRequest setPredicate:predicate];

现在在我的应用程序的另一个位置,我像这样设置已删除的属性(伪代码):

myManagedObject.deleted = YES
saveDataContext

当我返回 TableViewController 时,它仍然显示这个“已删除”的行。

当我尝试重新加载表格视图时,没有任何反应。

当我尝试使用 performFetch 重新加载 fetchedResultsController 时,它显示:

'致命错误:部分信息的持久缓存与当前配置不匹配。你非法改变了 NSFetchedResultsController 的获取请求、谓词或排序描述符,而没有禁用缓存或使用 +deleteCacheWithName:'

如果我删除缓存,在 init 方法中,调用performFetch,然后调用[myTable reloadData] 就可以了。

难道没有更简单的方法来刷新数据吗?最好是允许您使用 NSFetchedResultsController 的缓存功能的一种?

据我所知,我修改获取请求、谓词或排序描述符的唯一位置是在分配和初始化 NSFetchedResultsController 的同一方法中,因此它显示的错误消息似乎不正确。

更新:现在我对NSFetchedResultsController 有了更好的理解,我知道它不会自动为您删除行,主要负责删除的是controller:didChangeObject:atIndexPath:forChangeType:nowIndexPath: 方法行。我实现了这个方法,因为我的项目使用了 Apple 的模板。

但是,就我而言,我实际上并没有删除一个项目,我只是更新了一个属性 (deleted) 以指定该项目不应再存在于列表中。这意味着controller:didChangeObject:atIndexPath:forChangeType:nowIndexPath: 方法的更改类型是NSFetchedResultsChangeUpdate 而不是NSFetchedResultsChangeDelete。我将代码更新为:

  case NSFetchedResultsChangeUpdate: {
    MyObj *obj = (MyObj *)anObject;
    if (obj.deletedValue) { // NOTE: deletedValue returns the deleted property as BOOL
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    } else {
      [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
      break;
    }
  }

问题在于我收到错误消息:

无效更新:第 0 节中的行数无效。更新 (3) 后现有节中包含的行数必须等于更新前该节中包含的行数 (3),加上或减去从该部分插入或删除的行数(插入 1 行,删除 2 行)。与 userInfo (null)

基本上是抱怨 NSFetchedResultsController 中的对象数量和表格中的单元格数量不同步。

希望这样可以更清楚:

// DB Items (Name, Deleted):
[Foo, NO]
[Bar, NO]
[Baz, NO]

// mark one as deleted
Objects[1].deletedValue = YES;

// DB items now look like so:
[Foo, NO]
[Bar, YES]
[Baz, NO]

即使 NSFetchedResultsController 的 NSFetchRequest 的 NSPredicate 设置为 deleted == NO,NSFetchedResultsController 仍然看到 3 个项目而不是 2 个。

我该如何解决这个问题?我需要以某种方式刷新 NSFetchedReultsController 吗?还有什么问题?

【问题讨论】:

  • 我也面临同样的问题,虽然我没有使用任何保留字如删除。还有其他可能吗?

标签: objective-c cocoa-touch core-data nsfetchedresultscontroller nspredicate


【解决方案1】:

问题是我没有意识到deleted 在这种情况下是一个保留字。我不得不重命名该字段,它工作正常。

【讨论】:

  • 可惜我只能点赞一个!
  • 有这个确切的问题。以前的属性名称被“删除”;更改为“隐藏”后,FRC 会触发相应的更新。
  • 我遇到了完全相同的问题。废话,多么可怕的错误。文档中的保留字是否在任何地方?感谢您的修复。
  • 太棒了......我遇到了同样的问题。你到底是怎么知道deleted是一个关键词的?
【解决方案2】:

您需要为NSFetchedRequestController 设置一个代理以跟踪更改。要处理批量更新,您需要覆盖三个方法:

  • controllerWillChangeContent: - (此处设置索引路径列表)
  • controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: - (在此处存储每个呼叫)
  • controllerDidChangeContent: (在此处执行对 UITableView 的更新。)

对于更简单的情况,您知道每次只更新一个行,然后选择这样的方法:

-(void)controller:(NSFetchedResultsController *)controller 
  didChangeObject:(id)anObject 
      atIndexPath:(NSIndexPath *)indexPath 
    forChangeType:(NSFetchedResultsChangeType)type 
     newIndexPath:(NSIndexPath *)newIndexPath;
{
    switch (type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                                  withRowAnimation:UITableViewRowAnimationTop];
            break;
        case NSFetchedResultsChangeDelete:
            [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                                  withRowAnimation:UITableViewRowAnimationTop];
            break;
        case NSFetchedResultsChangeUpdate:
            [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                                  withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

【讨论】:

  • 我已经实现了这个方法,因为我使用的是苹果的模板。不过,我应该更明确地说明它。我更新了问题,希望它更容易理解。
  • 抱歉回复太晚了。您是否在后台线程上将对象标记为已删除?在这种情况下,您还需要在主线程的托管对象上下文中合并更改。
【解决方案3】:

从错误消息中我似乎很清楚,NSFetchedResultsController 不希望您在创建它后更改其获取请求。

您需要做的是在初始化NSFetchedRequestController 之前将谓词添加到获取请求中。如果您想在应用程序运行时更改获取请求的谓词,您可能需要创建一个新的 NSFetchedRequestController 并完全替换旧的。

【讨论】:

  • 我更新了问题以澄清 NSPredicate 是在 NSFetchedRequestController 初始化之前添加的。
猜你喜欢
  • 1970-01-01
  • 2011-12-12
  • 1970-01-01
  • 2011-03-24
  • 2012-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-11
相关资源
最近更新 更多