NSSortDescriptor 按核心数据对多关系中的项目数排序

Posted

技术标签:

【中文标题】NSSortDescriptor 按核心数据对多关系中的项目数排序【英文标题】:NSSortDescriptor to sort by number of items in Core Data To-Many Relationships 【发布时间】:2013-03-21 14:52:17 【问题描述】:

这是一个长期存在的问题,当使用 Core Data to-many-relationships 时,很难根据 children 的数量在一个 Parent 实体上使用 NSSortDescriptor 对获取请求进行排序。与Child 实体的一对多关系。这与NSFetchedResultsController 结合使用特别有用。通常将排序描述符初始化为:

NSSortDescriptor *sortByNumberOfChildren = [[NSSortDescriptor alloc] initWithKey:@"children.@count" ascending:NO];

导致异常'Keypath containing KVC aggregate where there shouldn't be one; failed to handle children.@count

iOS 6.1 上,我通过将 KVO 访问器 -countOf<Key> 作为属性添加到我的托管对象模型作为整数类型发现了一个修复方法。我没有在我的NSManagedObject 子类中为这个属性实现任何东西,因为所有的魔法似乎都发生在幕后。 (见https://***.com/a/15546371/2042527)。

但是,这不适用于 iOS 6.0。在这里,我发现将以下方法添加到您的 NSManagedObject 子类可以解决问题:

- (NSUInteger)countOfChildren
      return [self.children count];
  

同时添加两者不会解决两个 SDK 中的问题。相反,它会破坏修复。

有没有人知道为什么会发生这种情况以及为什么两者之间存在差异,尽管没有提到 ios 6.0 和 iOS 6.1 之间对 Core Data 或 Foundation 的更改。

【问题讨论】:

您已将此添加到NSManagedObjectModel?很难看出它是如何编译的,更不用说工作了。 NSManagedObjectModel 与其他类没有任何关系。 对不起,我的错,我的意思是NSManagedObject 子类也是第二次。我已经纠正了错误。 不是解决您的确切问题,而是另一种观点:如何获取孩子并计算不同父母的数量?也许这个post 对你有帮助。 【参考方案1】:

我认为通过说“包含 KVC 聚合的 Keypath 不应该有;未能处理 children.@count”Core Data 想告诉你它不支持这种排序描述符。这很可能是因为当支持 SQLite 存储收到您的 fetch 请求时,它必须生成执行 fetch 请求所描述的 SQL。 “children.@count”的情况实际上比人们想象的要复杂。

覆盖 -countOfChildren 的“修复”并不是真正的修复。让我们假设这解决了问题,然后 -countOfChilden 将在每个父级上调用。当您第一次访问 self.children 时,Core Data 需要执行一个 SQL 查询来确定(至少)孩子的主键,创建 NSManagedObjectIDs、NSManagedObjects 并返回结果。如果这行得通,那么您会看到非常糟糕的性能。

您的问题有多种解决方案。

1.将子计数存储在持久属性中

只需向您的父实体添加一个属性(名称:cachedCountOfChildren,类型:64 位整数)。在您的控制器层(不在您的模型层)中,每次将子项分配给父项时将 cachedCountOfChildren 递增 1,并在每次从父项中删除子项时递减 cachedCountOfChildren。然后在排序描述符键中使用 cachedCountOfChildren。这将有很好的表现。

2。使用字典结果

将 NSFetchRequest 的 resultType 设置为 NSDictionaryResultType。这将导致 -executeFetchRequest:error: 返回 NSDictionaries 而不是 NSManagedObjects。带有 NSDictionaryResultType 的 NSFetchRequest 可以做不同的事情。例如,您可以使用 setPropertiesToGroupBy 和 NSExpression (...)。请查看 WWDC 会议“Using iCloud with Core Data (2012)”(从幻灯片 122 开始)以供参考。基本上,它们向您展示了如何构造一个请求,该请求将返回一个包含具有这种结构的字典的数组:

(
 
  countOfChildren = 1;
  parentName = "hello";
 ,
 
  countOfChildren = 134;
  parentName = "dsdsd";
 ,
 
  countOfChildren = 2;
  parentName = "sdd";
 
)

如您所见,您将得到一个未排序的结果。但是通过 countOfChildren 对这个数组进行排序可以非常有效地在内存中完成。在这种情况下,Core Data 生成的 SQL 也将非常有效,您将能够准确指定字典应包含哪些属性。所以结果也应该是非常有效的内存。此解决方案的优点是您不必跟踪 countOfChildren。

您必须根据自己的情况决定哪种解决方案最适合自己。

【讨论】:

为什么不在 NSManagedObject 子类(即模型层)上实现这个? 实施什么? 1 还是 2? 我想知道为什么你为 1 写了以下内容:In your controller layer (NOT IN YOUR MODEL LAYER) increment cachedCountOfChildren by 1 every time you... 因为在一个完美的世界里,你的 NSManagedObjects 应该非常简单。这是因为许多原因(性能、复杂性……)。在你的子类中正确地做聪明的事情是非常困难的。例如,由于所有内存中的属性都设置为 nil,当对象变成故障时,您会收到“children”的 KVO 通知。并非所有属性更改都是由您或用户引起的更改。上下文可以将对象变成错误,您可以更改子项的数量。

以上是关于NSSortDescriptor 按核心数据对多关系中的项目数排序的主要内容,如果未能解决你的问题,请参考以下文章

可以将“self”与 NSSortDescriptor 一起使用来按对象本身而不是对象的属性进行排序? (核心数据/NSFetchedResultsController)

核心数据和 NSSortDescriptor 未根据基于 NSString 的第三个描述符排序

用于核心数据一对多关系的 NSSortDescriptor

核心数据 - 可以使用 NSSortDescriptor 中的块对 TableView 进行排序吗?

NSSortDescriptor 和多对多关系

iPhone:无法对多对关系进行 NSSortDescriptor。如何排序?