class不是密钥值@sum的编码兼容

时间:2015-02-20 06:05:36

标签: objective-c cocoa core-data

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

我决定为运行总和添加一个新列,并使用我所见过的@sum。无论我做什么,我都会收到错误。

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

在主窗口上,我有NSTableViewNSArrayController的控制器,一个用于商店,一个用于商品。项目NSArrayController的内容由商店控制器控制 - 所选项目。

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

我可能遗漏了一些简单的想法,如何正确地做到这一点?

谢谢。

- [编辑] ----

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

  • ManagedObjectContext
    • 绑定到主控制器的ManagedObjectContext

项目NSArrayController

  • 对象控制器

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

    • 绑定到存储阵列控制器

    • 控制器键:选择

    • 模型关键路径:项目

项目NSTableView

第一栏:

  • 绑定到项目数组控制器

  • 控制器密钥:arrangeObjects

  • 模型关键路径:名称

第二栏:

  • 类似,型号关键路径:价格

新的总和栏目:

  • 绑定到项目数组控制器

  • 控制器密钥:arrangeObjects

  • 模型关键路径:@ sum.price

我收到的错误是:“实体项目与密钥”@sum“不符合密钥值编码。”

1 个答案:

答案 0 :(得分:1)

考虑你的第一栏。它与项目控制器arrangedObjectsname绑定。每个单元格都有一个名称数组吗?不。每个人都有一个名字。

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

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

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

您还可以将文本字段绑定到项目控制器 [ItemController valueForKeyPath:@"arrangedObject.@sum.price"] selection,以显示所选项目的价格总和在项目表中。

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


更新

要获得运行总和,您需要不对列使用绑定。如果尚未使用@sum.price,请使您的视图或窗口控制器采用。然后将表格视图的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"]; 关键路径self的变化。您可以在@"itemController.arrangedObjects.price"-viewDidLoad中进行设置。当控制器完成时,不要忘记把它拆掉。

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

这应该可行但是一旦你获得了大量的行,它将会非常低效。

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

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

要构建缓存,请迭代-tableView:objectValueForTableColumn:row:计算运行总和,并将每个值添加到数组的末尾。您可以根据需要使用C样式的基本类型数组或self.itemController.arrangedObjects NSMutableArrayNSNumber。 (通过使用NSMutableData作为缓冲区,可以简化C风格数组的内存管理。)

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

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