是否每个核心数据关系都必须有一个反转?

假设我有两个 Entity 类: SocialAppSocialAppType

SocialApp中,我有一个属性: appURL和一个关系: type

SocialAppType中我有三个属性: baseURLnamefavicon

SocialApp关系 type的目的地是 SocialAppType中的一条记录。

例如,对于多个 Flickr 帐户,会有许多 SocialApp记录,每条记录都保存着指向某个人帐户的链接。将有一个“ Flickr”类型的 SocialAppType记录,所有 SocialApp记录都将指向该记录。

当我使用此模式构建应用程序时,会得到一个警告,说明 SocialAppTypeSocialApp之间没有反向关系。

 /Users/username/Developer/objc/TestApp/TestApp.xcdatamodel:SocialApp.type: warning: SocialApp.type -- relationship does not have an inverse

我需要倒数吗,为什么?

62909 次浏览

实际上,我并没有因为没有反转而丢失任何数据——至少我是这么认为的。一个快速的谷歌建议你应该使用它们:

反向关系不仅仅是 让东西更整洁,实际上 核心数据用于维护数据 正直。

—— Cocoa Dev 中心

你应该做个典型的模特 双向的关系,以及 指定反向关系 核心数据使用这个 资料,以确保一致性 如果更改为 制造(见「操纵关系」 和对象图完整性”) 讨论一些原因 你最好不要做模特 双方的关系,和 可能出现的一些问题 如果没有,请参阅“单向 关系”

—— 核心数据编程指南

更好的问题是,“ 没有有反转的原因吗?”?核心数据实际上是一个对象图管理框架,而不是一个持久化框架。换句话说,它的工作是管理对象图中对象之间的关系。反向关系使这种 很多更容易。出于这个原因,CoreData 期望反向关系,并且是为该用例编写的。没有它们,您将不得不自己管理对象图的一致性。特别是,没有反向关系的 to-many 关系很可能被 Core Data 破坏,除非您努力工作 非常以保持工作状态。与它带来的好处相比,反向关系在磁盘大小方面的成本实际上是微不足道的。

我将转述我在 更多 iPhone3开发中找到的戴夫 · 马克和杰夫 · 勒马尔彻的确切答案。

苹果通常建议你总是创建和指定相反的关系,即使你没有在你的应用程序中使用相反的关系。由于这个原因,当你没有提供一个反向时,它会发出警告。

关系不是 需要的反向关系,因为在一些情况下,反向关系可能会损害性能。例如,假设反向关系包含极大数量的对象。移除反向需要遍历表示反向的集合,从而削弱性能。

但是 除非你有特别的理由不这样做,否则建立相反的模型。它有助于核心数据确保数据的完整性。如果遇到性能问题,稍后可以相对容易地消除反向关系。

苹果的文档中有一个很好的例子,它提示了一种情况,在这种情况下,没有反向关系可能会出现问题。让我们把它映射到这个案子里。

假设您将其建模如下: enter image description here

请注意,您有一个名为“ 类型”的 1比1关系,从 SocialAppSocialAppType。这个关系是 没得选择,有一个 “拒绝”删除规则

现在考虑以下几点:

SocialApp *socialApp;
SocialAppType *appType;
// assume entity instances correctly instantiated


[socialApp setSocialAppType:appType];
[managedObjectContext deleteObject:appType];
BOOL saved = [managedObjectContext save:&error];

我们希望这个上下文失败,因为我们已经将 delete 规则设置为 Deny,而关系是非可选的。

但这次救援成功了。

原因是我们还没有设置 反向关系。因此,在删除 appType 时,socialApp 实例不会被标记为已更改。因此,在保存之前,socialApp 不会进行任何验证(它假设不需要验证,因为没有发生任何更改)。但实际上发生了变化。但它没有被反射。

如果我们通过

SocialAppType *appType = [socialApp socialAppType];

AppType 为空。

很奇怪,不是吗? 我们得到一个非可选属性为零?

所以如果你建立了相反的关系,你就不会有麻烦。 否则,您必须通过编写以下代码来执行强制验证。

SocialApp *socialApp;
SocialAppType *appType;
// assume entity instances correctly instantiated


[socialApp setSocialAppType:appType];
[managedObjectContext deleteObject:appType];


[socialApp setValue:nil forKey:@"socialAppType"]
BOOL saved = [managedObjectContext save:&error];

至少有一种情况可以很好地解决核心数据关系而不需要反向: 当两个对象之间已经存在另一个核心数据关系时,将处理维护对象图。

例如,一本书包含许多页,而一页在一本书中。这是一种双向多对一的关系。删除页面只会使关系无效,而删除图书也会删除该页面。

但是,您也可能希望跟踪每本书当前正在阅读的页面。这可以通过 翻页上的“ currentPage”财产来完成,但是还需要其他逻辑来确保在任何时候书中只有一页被标记为当前页。相反,将 currentPage 关系从 Book 设置为单个页面将确保始终只有一个当前页面被标记,而且这个页面可以通过引用 Book.currentPage 轻松访问。

Graphical presentation of core data relationship between books and pages

在这种情况下,互惠关系是什么?一些很荒谬的东西。“ myBook”或类似的东西可以向另一个方向添加,但是它只包含页面的“ book”关系中已经包含的信息,因此会产生自己的风险。也许在将来,您使用这些关系之一的方式会发生变化,从而导致您的核心数据配置发生变化。如果 page.myBook 在某些应该在代码中使用 page.book 的地方使用过,那么可能会出现问题。另一种主动避免这种情况的方法是不要在用于访问页面的 NSManagedObject 子类中公开 myBook。然而,我们可以说,首先不建立反向模型更简单。

在所概述的示例中,currentPage 关系的删除规则应设置为“ No Action”或“ Cascade”,因为与“ Nullify”没有相互关系。(Cascade 暗示当你读这本书的时候,你正在撕掉它的每一页,但是如果你特别冷并且需要补充能量的话,这可能是真的。)

当可以证明对象图的完整性不存在风险时,如本例所示,并且代码复杂性和可维护性得到改善,可以认为没有逆的关系可能是正确的决策。


正如注释中所讨论的,另一种解决方案是在目标上创建您自己的 UUID 属性(在这里的示例中,每个 Page 都有一个 ID 是 UUID) ,将其存储为属性(currentPage 只是将 UUID 作为一个属性存储在 Book 中,而不是作为一个关系) ,然后编写一个方法在需要时获取具有匹配 UUID 的 Page。这种方法可能比使用没有反向关系的关系更好,至少因为它避免了所讨论的警告消息。

虽然文档似乎不需要反转,我只是解决了一个场景,实际上导致“数据丢失”没有反转。我有一个对可报告对象具有对多关系的报告对象。如果没有反向关系,那么在重新启动时,对 to-many 关系的任何更改都将丢失。在检查了 Core Data 调试之后,很明显,即使我正在保存报告对象,也从未对对象图(关系)进行过更新。我加了一个反转,虽然我没有用,瞧,成功了。所以它可能不会说这是必须的,但是没有倒数的关系肯定会有奇怪的副作用。

Object Integrity中也使用逆元(因为其他原因,请参阅其他答案) :

推荐的方法是在两个方向上建立关系模型 并适当地指定反向关系 这些信息,以确保对象图的一致性,如果 已经改变了

发信人: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/HowManagedObjectsarerelated.html#//apple_ref/doc/uid/TP40001075-CH17-SW1

提供的链接给你的想法,为什么你应该有一个 inverse集。没有它,您可能会失去数据/完整性。另外,访问 nil对象的可能性更大。

一般不需要逆关系。但是在核心数据中,需要反向关系的地方很少出现异常/错误。有些情况下,关系/对象丢失了,即使在保存上下文时没有错误,如果缺少反向关系。检查这个 例子,我创建它是为了演示在处理 Core 数据时缺少对象以及如何解决这个问题