【问题标题】:class is not key value coding-compliant for the key @sum类与键 @sum 的键值编码不兼容
【发布时间】:2015-02-20 06:05:36
【问题描述】:

好的,所以如果你必须投反对票,但我已经准备好完成一些事情,因为这让我发疯了。我有一个core data 应用程序(OS X),有一对NSTableViews(基于单元)通过NSArrayControllers 连接。我有一些自定义方法的实体类设置。我可以使用数据添加、删除、编辑和做各种事情——一切都很好。

我决定为运行总和添加一个新列,并使用@sum,正如我所看到的那样。无论我做什么,我都会不断收到错误消息。

我有一个实体“Store”和另一个实体“Item”,它们有一对多的关系。在 Item 实体中,我有一个名称和价格属性。

在主窗口上,我有两个NSArrayControllers 的NSTableView 控制器,一个用于商店,一个用于物品。 Item NSArrayController 的内容由 Store Controller 控制 - 所选项目。

我向 Item NSTableView 添加了一个新列,将其绑定到 Item Controller 并将其模型键路径设置为 @sum.price - 这会导致错误。

我可能遗漏了一些简单的东西,关于如何正确执行此操作的任何想法?

谢谢。

--[编辑]----

商店NSArrayController: - 对象控制器 - 实体名称:商店

  • ManagedObjectContext
    • 绑定到主控的ManagedObjectContext

项目NSArrayController:

  • 对象控制器

    • 实体名称:项目
  • 内容集:

    • 绑定到存储阵列控制器

    • 控制器键:选择

    • 模型键路径:项目

项目NSTableView:

第一列:

  • 绑定到项目数组控制器

  • 控制器键:排列对象

  • 模型密钥路径:名称

第二栏:

  • 类似,模型键路径:价格

新的总和列:

  • 绑定到项目数组控制器

  • 控制器键:排列对象

  • 模型密钥路径:@sum.price

我收到的错误是:“实体项与键“@sum”的键值编码不兼容。”

【问题讨论】:

  • 您需要清楚具体地了解绑定。例如,“NSArrayController 项目控制器的内容绑定绑定到 NSArrayController 存储控制器,控制器键 selection,模型键路径 items。新列的内容绑定绑定到项目控制器,控制器键? ??,模型密钥路径@sum.price。”或者无论实际情况如何。哪些对象的哪些绑定绑定到什么,使用什么控制器键和什么模型键路径?此外,您在错误中留下了一些信息,例如哪个类不符合 KVC。
  • @KenThomases 谢谢,请参阅编辑。

标签: objective-c cocoa core-data


【解决方案1】:

考虑您的第一列。它绑定到项目控制器arrangedObjectsname。每个单元格是否都有一个名称数组?不。每个人都有一个名字。

虽然该列绑定有时表示为像Item Controller.arrangedObjects.name 这样的键路径,但它实际工作的方式是,列作为一个整体显示arrangedObjects,每行一个元素,但name 应用于每个元素该集合的元素分开。因此,每个单元格都有一个名称。

现在考虑您的新专栏。这些行再次对应于项目控制器的arrangedObjects,但模型键路径单独应用于每个元素。但是模型键路径包含一个集合运算符@sum,它不适用于单个元素(Item 实体)。因此出现错误。

您可以创建一个文本字段(在表格之外),显示所选商店所有商品的价格总和。您可以将文本字段的值绑定绑定到项目控制器arrangedObjects@sum.price。文本字段的工作方式与表格列不同,因为它只有一个要显示的内容。它确实使用了[ItemController valueForKeyPath:@"arrangedObject.@sum.price"] 的结果。集合运算符将应用于集合。

您还可以将文本字段绑定到项目控制器,selection@sum.price 以使其显示选定中项目的价格总和项表。

绑定不提供任何方法来获得运行总和,如果我理解你的意思(第一行显示第一个项目的价格,第二行显示第一个和第二个项目的价格总和, ETC。)。这样的运行总和将取决于上下文。给定行的值将取决于先前行的值。例如,对表格进行不同的排序意味着给定项目旁边的运行总和会发生变化,因为它之前的项目集已经改变。绑定无法做到这一点。他们不知道位置、索引或兄弟姐妹。


更新:

要获得运行总和,您不需要对列使用绑定。如果还没有,让您的视图或窗口控制器采用NSTableViewDataSource。然后将表格视图的dataSource 出口连接到它。

在您的数据源类中,实现-tableView:objectValueForTableColumn:row:。检查列identifier。对于除运行总和列之外的任何列,返回 nil,以便它使用列绑定中的值。

对于运行总和列,直接但低效的实现类似于:

NSRange range = NSMakeRange(0, rowIndex + 1);
NSArray* rowsToSum = [self.itemController.arrangedObjects subarrayWithRange:range];
return [rowsToSum valueForKeyPath:@"@sum.price"];

当需要重新加载(重新计算)运行总和列中的单元格时,您还需要一种方法来通知表格视图。您将使用 Key-Value Observing 来观察 self 以了解键路径 @"itemController.arrangedObjects.price" 的变化。您可以在-viewDidLoad-windowDidLoad 中进行设置。控制器完成后不要忘记将其拆除。

发送更改通知时(即调用 -observeValueForKeyPath:ofObject:change:context: 时)您将在表视图上调用 -reloadDataForRowIndexes:columnIndexes: 以指示应重新加载运行总和列中的所有行索引。

这应该可以,但是一旦获得大量行,它的效率就会非常低。

因此,要进行优化,您应该缓存运行总和,但您需要注意适当地使缓存无效。

基本上,有一个像_cacheIsValid 这样的实例变量。像所有实例变量一样,默认情况下它会从零开始(假)。在-tableView:objectValueForTableColumn:row: 中,您将检查它是否有效。如果不是,您将构建它并记录它是有效的。然后,或者如果它已经有效,则只返回请求行的元素。

要构建缓存,迭代self.itemController.arrangedObjects 计算运行总和,并将每个值添加到数组的末尾。您可以根据自己的喜好使用 C 风格的原始类型数组或 NSNumbers 的 NSMutableArray。 (通过使用 NSMutableData 作为缓冲区,可以简化 C 样式数组的内存管理。)

在告诉表视图重新加载正在运行的总和列之前,您将使 -observeValueForKeyPath:... 中的缓存无效。

为了提高效率,您可能会在那时重新计算缓存并将值与现有缓存(如果有效)进行比较。在NSMutableIndexSet 中仅累积缓存运行总和实际更改的那些行的行索引,并在对-reloadDataForRowIndexes:columnIndexes: 的调用中使用它。这样,表格视图只会重新加载实际更改的单元格。

【讨论】:

  • 好的,我明白你在说什么,并且我已经让 @sum 与 textField 一起工作 - 所以我误解了 (at)sum 可以做什么,真可惜。是的,你对我的运行总和的意思是正确的。关于如何实现这一点的任何想法?
  • 我已经更新了我的答案,提出了如何实施运行总和列的建议。
  • 我知道你的目标是什么,这确实有效 - 但你说得对,它效率低下。感谢您的帮助。
  • 明确地说,只有幼稚的原始实现效率低下。我相信在我建议的优化实施后,整体方案将非常有效。
  • 同意,我也指的是未优化的版本。再次感谢您。
猜你喜欢
  • 2013-10-11
  • 2013-05-23
  • 2017-09-27
  • 1970-01-01
  • 1970-01-01
  • 2015-08-27
  • 2017-01-23
  • 2011-03-17
  • 2012-09-24
相关资源
最近更新 更多