是否可以从 NSFetchedResultsController 获取最新快照

Posted

技术标签:

【中文标题】是否可以从 NSFetchedResultsController 获取最新快照【英文标题】:Is it possible to source an up-to-date snapshot from an NSFetchedResultsController 【发布时间】:2020-06-01 23:11:17 【问题描述】:

ios 13 开始,使 UITableViewNSFetchedResultsController 保持同步的最简单方法似乎是使用快照。

每当 managedObjectContext 报告添加、删除或更新时,NSFetchedResultsController 都会向其委托提供快照引用。使用快照 (NSDiffableDataSourceSnapshot) 时,只需实现一种 FRC 委托方法:controller(_:didChangeContentWith:)。为了使该委托方法工作,UITableViewDiffableDataSource 和快照必须输入<String, NSManagedObjectID>

它可以工作mostly。

但是如果需要更新整个表怎么办?使用 tableView.reloadData()frc.performFetch() 似乎是反模式。

编辑

我手动构建了一个快照,并在必要时调用 apply。但是由于我的快照是基于 NSFetchedResultsSectionInfo 对象的,所以我似乎在复制 FRC 已有的内容:Hashable 部分标题和 Hashable NSManagedObjectIDs

【问题讨论】:

SmallTalk,我不明白你的问题: UITableView 和 NSFetchedResultsController 工作绝对完美。 (他们当然会这样做,否则许多主要应用程序会崩溃:-))我对您为什么还要使用快照感到困惑?为什么?是什么原因?你能帮我理解吗? 嗨 Fattie,谢谢支持。我的问题是:如果需要更新整个 UITableView,生成新的NSDiffableDataSourceSnapshot 的最佳做法是什么?我希望我能从 FRC 中“抓住它”。 FRC 现在可以提供 NSDiffableDataSourceSnapshotReference。但看来我必须通过自己的快照手动构建。由于我的快照是从 NSFetchedResultsSectionInfo 对象构建的,因此我似乎在复制 FRC 已有的内容:可散列的部分标题和可散列的 NSManagedObjectIDs 我使用快照是因为我能够消除大约 6 个 FRC 委托方法,而只使用一个。正如 Apple 的“controller(_:didChangeContentWith:) 文档”所说:“如果实现了此方法,则不会调用其他委托方法。”所以 UITableViewDiffableDataSource+UITableView 超级性感,有漂亮的动画,并且消除了大量的样板代码。但我仍在试图理解为什么我必须“滚动我自己”的快照,如果 FRC 可以自己生成一个。 smalltalk @smallTalk,非常感谢您提供的信息 - 我将彻底调查您所说的一切! 【参考方案1】:

对于我之前(已删除)的答案,我深表歉意。快照与 Core Data 上下文无关。

NSFetchedResultsControllerCore Data 结合使用的目的是在保存 NSManagedObjectContext 时更新 UI。

为了能够控制 diffable 数据源的动画(解决荒谬的行为),您必须继承 UITableViewDiffableDataSource 并添加属性 animatingDifferences。进一步在子类中采用NSFetchedResultsControllerDelegate不是在视图控制器中)。

class DiffableCoreDataSource: UITableViewDiffableDataSource<String,NSManagedObjectID> 
    var animatingDifferences = false


extension DiffableCoreDataSource : NSFetchedResultsControllerDelegate

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) 
        apply(snapshot as NSDiffableDataSourceSnapshot<String, NSManagedObjectID>, animatingDifferences: animatingDifferences)
        animatingDifferences = true // set it to the default
    

在视图控制器中,将 FRC 的 delegate 设置为子类,假设有一个代表 DiffableCoreDataSourcedataSource 属性

frc.delegate = dataSource

如果记录更新,则在保存上下文之前将dataSource.animatingDifferences 设置为false

要重新加载整个表视图调用frc.performFetch()。永远不要在表格视图上调用reloadData()

【讨论】:

您好 Vadian,感谢您抽出宝贵时间回答。我将对此进行实验。最后一句真的是我所追求的;如果performFetch() 在功能上等同于创建自己的NSDiffableDataSourceSnapshot 并在需要更新整个表时应用它,那么这将是公认的答案。 (与 CoreData 和 FRC 一起管理快照似乎是一种不直观的方法。但这可能是 Apple 目前为我们提供的。) “永远不要在表格视图上调用 reloadData()。”我不能同意。重新加载数据是使表格显示单元格内容更改的唯一方法(因为 Hashable/Equatable 的定义方式)不构成“不同”单元格。此处的解决方案不应对同时更改记录和创建/删除另一个记录的可能性。例如,请参阅***.com/questions/62267256/…。 @matt reloadData()UITableViewDiffableDataSource 驱动的上下文中是什么?标准实现中没有cellForRowAt @vadian 这就是我对链接问题的回答所证明的。 @matt 我理解,但是对于 Core Data 和 NSFetchedResultsControllerDelegate,您通常不会自己 apply 快照。上下文将调用委托方法的更改通知 FRC。在我看来,控制animatingDifferences 参数比重新加载表格视图更有效。【参考方案2】:

TL;DR:即使 NSFetchedResultsController 能够在 managedObjectContext 报告添加、删除和更新时为其委托提供最新的快照引用,但(至少目前)无法以编程方式访问直接来自 FRC 的快照。

正如 vadian 建议的那样,使用 FRC 实例方法 performFetch() 将最新快照应用于 DiffableData 支持的 UITableview 中的所有单元格。这是一种未记录的快照管理方法。但它可以单独使用 FRC 提供的快照,而不必编写独立的快照。更少的代码,以及用于快照更新的“单一真实来源”。

根据其他人的反馈,缺点是apply(_: animatingDifferences:) 方法中的animatingDifferences 参数如果设置为true 会触发错误。两个可重现的错误是第一次应用快照时表根本无法加载。如果尝试使用trailingSwipeActionsConfigurationForRowAt 方法或其他方法删除记录,则另一个错误是应用程序崩溃。因此,使用这种后门方法的唯一方法是在所有情况下将该 bool 设置为 false。这意味着,嗯,没有动画。

【讨论】:

闲聊感谢这个sn-p。现在,要更新单元格,我使用的是 NSManagedObjectContext。我希望这种方法是正确的:让 model = self.viewContext.object(with: objectID) as! MyModel cell.configureCell(with: model, animated: false) 您好 AgentX 大师,如果您遇到问题,我会将其作为单独的问题发布。 1) 据我了解,在 MOC save 操作之前,objectID 不会永久存在,因此请确保在配置任何单元之前已调用 save。其次,UITableViewDiffableDataSource init 方法直接出售托管对象,所以我推荐该路径。见wwdcbysundell.com/2019/diffable-data-sources-first-look。 (在这种情况下,在 init 闭包中找到的托管对象来自 performFetch() 中生成的“秘密”NSFetchedResultsController snapshot,如上。

以上是关于是否可以从 NSFetchedResultsController 获取最新快照的主要内容,如果未能解决你的问题,请参考以下文章

是否可以断定 MySQL 是否从 PhPMyAdmin 连接错误页面运行?

是否可以从成员函数中检测对象是否是临时对象?

是否可以从jstree拖放到jqgrid?

是否可以从 AppDelegate 获取 NSArray?

是否可以从功能文件运行测试场景?

是否可以从 QListWidgetItem* 中提取行