核心数据中的 Fetched Property 返回不正确的计数

Posted

技术标签:

【中文标题】核心数据中的 Fetched Property 返回不正确的计数【英文标题】:Fetched Property in core data returning improper count 【发布时间】:2011-12-13 20:47:54 【问题描述】:

我有一个如下所示的核心数据关系

项目A ->> 项目B

其中 itemA 有许多 itemB。我想使用一个获取的属性,该属性允许我获取与 itemA 关联的所有 itemB 关联,该 itemA 的 int32 状态属性设置为“2”。所以我在数据建模器中创建了一个获取的属性,它具有以下内容:

获取的属性:completedItem 谓词:状态 == 2 目的地:项目B

当我第一次尝试时,我拿回了物品,我认为这一切都很酷并且已经完成,然后我注意到奇怪的行为,当我仔细观察时,它返回的物品与实际数量的 itemB 无关与 itemA 对象相关联。更奇怪的是返回类型是 NSFaultingMutableArray。这是一个简单的例子

ItemA 有 0 个 itemB 对 ItemB 的 NSSet 属性 ItemA 的过滤谓词搜索返回 0 获取的属性“completedItem”返回 4 个 ItemB 它返回的类型是 NSFaultingMutableArray

这在我的脑海里现在很奇怪,而且真的没有意义。有什么想法吗?

更新 1:

这里列出的 fetched 属性似乎获取了核心数据必须提供的与谓词匹配的 所有 ItemB 对象,即使它与相关的 ItemA 没有关联

【问题讨论】:

【参考方案1】:

这是本期所有奇怪问题的答案:

1) 获取的属性确实没有为 ItemA 返回 ItemB 对象。为了发生这种情况,您必须在 fetched properties 谓词中添加类似的内容

status == 2 AND ItemA == $FETCH_SOURCE

2) 来自 Fetched Properties 文档:

获取的属性被延迟评估,随后被缓存。

如果目标实体中的对象发生更改,您必须重新评估获取的属性以确保它是最新的。您使用 refreshObject:mergeChanges: 手动刷新属性 - 这会导致在下次触发对象故障时再次执行与此属性关联的获取请求。

所以基本上使用refreshObject:mergeChanges 手动刷新对象以重新加载获取的属性。您可以通过添加一个刷新方法或对您的子类 NSManagedObject 中的 KVC get 方法进行一些花哨的覆盖来做到这一点。

话虽如此,这里的其他人(Rob Booth,Grady Player)通过完全绕过获取的属性有其他有效的解决方案。虽然这些很糟糕

【讨论】:

【参考方案2】:

Kyle,问题在于 Core Data 正试图变得比你更聪明。如果您不打算使用数据存储区中的所有对象,Core Data 不想填满您的内存。因此,它不是一直创建真实对象,而是创建“故障”。错误是真实对象的占位符,当您请求这些对象时,Core Data 会在那时填充这些对象。

我最好的猜测是您对 ItemA.ItemB 属性的搜索没有实现链接对象,因此这就是您返回 0 集的原因。如果你只是做了一个 NSSet *mySet = ItemA.ItemB 我相信你会看到 mySet 包含正确的对象数量。但是对 ItemA.ItemB 进行计数会将计数消息发送到故障集,因此返回 0。

当您使用您的 completedItems 属性时,CoreData 似乎至少在做一些事情来返回正确数量的对象,但还不是实际的对象数据。

【讨论】:

嗨,罗伯!不过,仔细研究一下,它似乎正在计算数据库中与谓词匹配的所有 ItemB 对象。 所以您有 3 个与您的谓词匹配的 ItemB,但其中只有 2 个链接到 ItemA,您希望返回 2 个对象但返回 3 个。我说的对吗?如果是这样,我认为问题在于获取的属性没有考虑您的项目关系,而只是在做可能被视为子查询的事情。尝试将您的关系值添加到谓词 status == 2 && itemA == $FETCH_SOURCE?只要您有适当的反向关系设置,这应该可以工作。 在更多地研究获取的属性时,我认为这不是你正在做的事情的真正方法。我建议您只需拉出所有链接的 ItemB,然后通过您的谓词运行它。您可以使用 ItemA 的自定义 NSManagedObject 来执行此操作,或者只是在事后手动执行它。这是一个漂亮的小类别,用于向 NSSet 添加谓词:boenne.wordpress.com/2007/08/10/nsset-and-predicates【参考方案3】:

让我们使用这个术语:

我们有一个 ClassA 的 objectA

我们有一些 ClassB 的对象(未命名)

我们已将 ClassB 对象添加到 objectA。

objectA 是对 NSManagedObject 的子类的引用... 当您调用 objectA.completeItem 时,您是在向 objectA 询问 ItemB 的集合, 这将起作用,因为您正在使用内存中的一个实例。

当您创建一个新的 NSManagedObjectContext 并在其中执行 fetch 时,它对您的 objectA 或其关系一无所知,除非您在执行 fetch 之前对 objectA 的上下文执行保存。

编辑:

如果您的谓词只过滤出 ItemA 拥有的 classB 对象,您可以使用类似的谓词(假设 itemA 与您的 ItemA cpmlletedItem 关系相反)

[NSPredicate predicateWithFormat: @"itemA = %@",itemA];

【讨论】:

有点困惑这意味着什么 看起来更多的是,fetched 属性正在计算 ItemB 的 所有 个已完成的对象 == 2。而 filters 属性首先获取所有 ItemB 项目ItemA,然后过滤它。奇怪... 非常正确,但是我如何从获取的属性中做到这一点。老实说,如果我不能从取来的财产中做到这一点,他们为什么还要拥有它们呢? 叹息 非易失性!在文档中找到答案,实际上文档在理解文档方面提供了更多帮助,完成后会更新我的主要帖子

以上是关于核心数据中的 Fetched Property 返回不正确的计数的主要内容,如果未能解决你的问题,请参考以下文章

使用 fetched 属性的核心数据跨存储查询

SwiftUI:如何从核心数据中的 TextField 中保存值

我的 Fetched Results Controller 没有在旧条目中对新条目进行排序

核心数据,排序搜索——有可能吗?

对 CoreData 中的 1 个条目使用 Fetched Results Controller

自定义验证期间神奇记录核心数据中的上下文保存问题