Creating Managed Object Relationships
一个managed对象和一个实体描述相关。(fNSEntityDescription
实例)提供对象相关数据和managed object context。数据包含实体名称,属性名称,属性之间的关系。
在一个已有的managed object context中,managed 对象提供了在持久化存储中的表述,但是可能有多个contexts。每个context包含一个分开的managed对象来描述这个record。也就是说在一个managed对象和数据record之间是一对一的关系,但是在一个record和managed 对象间却是一对多的关系。
core data不会让你创建跨存储的关系。如果你需要创建从一个对象到其它存储对象的关系,就要考虑使用weak relationship (fetched 属性)
Relationship Definitions in the Managed Object Model
当创建关系的时候需要注意几点。什么是destination实体?关系是一对一还是一对多?这个关系是optional的吗?如果是一对多的话,会不会定义最大或者最小数量的对象在这个关系中?当source对象被删除时会发生什么?
在一个对象模型关系中,你假如有一个 source实体,Department 和一个destination实体,比如Employee.你定义一个source实体和destination实体间的关系。
Figure 12-1Relationship pane in the Data Model inspector
Relationship Fundamentals
关系指定了实体或者parent实体。这个实体可以和source实体一致。如果employee实体有两个子实体,例如有manager和assistant,而且employee不是抽象类,那么一个给定的department的employee可能由employees(主实体),managers(employee子实体),assistants(employee 子实体),或者任何的组合来组成。
你可以置顶关系是一对多或者一对一。你也可以在定义一对多的关系时考虑设置对象个数的上限和下限。下限不能是零。你可以指明employees在department中的个数,必须在3和40之间。如果关系不是optional的话,那么就必须有一个或者多个objects在destination的关系中。optional意味着不需要在destination定义任何的对象,但是如果有的话,对象的数量必须在指定的范围内。
Creating Relationships Does Not Create Objects
需要注意的是当一个新的source对象创建的时候,简单定义关系不会导致destination对象的创建。所以需要考虑声明一个实例变量。
@interface AAAEmployeeMO :NSManagedObject
@property (nonatomic,strong)AAAAddressMO*address;
@end
class AAAEmployeeMO:NSManagedObject {
@NSManaged address: AAAAddressMO?
}
如果你创建了一个employee实例,除非你创建代码来创建address实例,否则他是不会被创建的。类似的,如果你定义了address实体,和一个从employee到address的非optional的一对多的关系,然后简单创建一个employee实例是不会创建一个新的address实例的。类似的,如果你定义了一个非optional的一对多的从employee到address的关系并且限定了数量为1,那么简单创建employee实例是不会创建一个新的address实例的。
Inverse Relationships
大多对象的关系是内在双向的。如果一个department有一个一对多的到employees的关系,那么就有一个从employee到department的关系。有一个例外,就是显示设置了weak one-way关系。
推荐使用双向关系。core data使用这个信息来确定如果有改变发生,对象图表的一致性。
Relationship Delete Rules
如果一个删除关系准则设置为deny,那么source对象就可能不会被删除。
Deny
拒绝-如果在destination(employees)关系中至少有一个对象存在,就不要删除source对象(department)
例如,如果你想移除department,你就必须所有的employees被transferred或者被解雇掉。否则,department不能被删除。
Nullify
作废-移除对象间的关系但是不删除对象。只有当department和employee的关系是optional的时候才有用。或者如果你确定你在下一个save操作前确保你为每一个employees设定了deparment。
Cascade
当删除source时,删除关系中的destination 对象们。
例如,如果你删除department,就要在同时fire掉在department中的所有的employees。
No Action
不会影响关系中的destination对象。
例如,如果你删除一个department,不要管所有的employees,甚至她们依然相信自己属于这个department。
Manipulating Relationships and Object Graph Integrity
当你修改一个对象图表时,保证完整性是非常重要的。core data使得你很容易来改变managed对象间的关系而且不会引起错误。
当你需要改变关系时,core data负责对象图表的完整性。因此你只需要改变最终的一个关系。这个特点适用于一对一,一对多,多对多的关系。以下面的例子为解释
如果一个新的employee被分到特定的manager,那么manager意识到这个责任是很重要的。这个新的employee必须添加到manager的reports列表中去。类似的,如果一个employee从一个department transferred到其它的department,就必须做一定数量的修改。employee的新的部门被设置了,employee被从employees列表中移除掉,并且employee被添加到新的department的employee列表中。
Figure 12-2Inverse relationship maintaining integrity
如果不用core data框架,你就必须写相应的代码来确保对象图表的完整性。而且你必须熟悉department类的实现来判断是否相反的关系应当从employee设置到新的department中。使用core data框架,所有这些可以通过单独的一行代码来实现:
anEmployee.department=newDepartment;
anEmployee.department =newDepartment
Alternatively, you can use:
[newDepartmentaddEmployeeObject:anEmployee];
newDepartment.mutableSetValueForKey("employees").addObject(employee)
所有这些都有同样的效果:通过引用应用程序的managed对象模型,框架自动确定当前的对象图表中哪些关系必须被确立,哪些关系必须被打破。
Many-to-Many Relationships
你使用两个对多的关系定义了一个多对多的关系。第一个对多的关系来自于第一个实体(source实体)到第二个实体(the destination)。第二个对多关系从第二个实体(the original destination实体)到第一个实体(the original entity)。然后你将她们互相设置为inverse。(如果你有一个后台管理数据库的操作可能会感觉到困扰。如果你使用sqlite存储,core data会自动为你创建内置的关联表格)
你必须定义双向的多对多的关系-你必须置顶两个关系,每一个是另外一个的inverse。你不能只是在一个方向上定义一个对多的关系并且尝试使用他作为多对多。如果你这样做了,会出问题。
例如,一个employee可以有多个manager
Figure 12-3Example of a reflexive many-to-many relationship
Modeling a Relationship Based on Its Semantics
Consider the semantics of the relationship and how it should be modeled. A common example of a relationship that is initially modeled as a many-to-many relationship that’s the inverse of itself is “friends”. Although you are your cousin’s cousin whether the cousins like it or not, it’s not necessarily the case that you are your friend’s friend. For this sort of relationship, use an intermediate (join) entity. An advantage of the intermediate entity is that you can also use it to add more information to the relationship. For example, a FriendInfo entity might include some indication of the strength of the friendship with a ranking attribute, as shown inFigure 12-4.
Figure 12-4A model illustrating a friends relationship using FriendInfo as an intermediate entity
In this example, Person has two to-many relationships to FriendInfo: friends represents the source person’s friends, and befriendedBy represents those who count the source as their friend. FriendInfo represents information about one friendship, in one direction. A given instance notes who the source is, and one person they consider to be their friend. If the feeling is mutual, then there will be a corresponding instance where source and friend are swapped. There are several other considerations when dealing with this sort of model:
-
To establish a friendship from one person to another, you have to create an instance of FriendInfo. If both people like each other, you have to create two instances of FriendInfo.
-
To break a friendship, you must delete the appropriate instance of FriendInfo.
-
The delete rule from Person to FriendInfo should be Cascade. That is, if a person is removed from the store, then the FriendInfo instance becomes invalid, so it must also be removed.
As a corollary, the relationships from FriendInfo to Person must not be optional—an instance of FriendInfo is invalid if the
source
orfriend
is null. -
To find out who one person’s friends are, you have to aggregate all the
friend
destinations of thefriends
relationship, for example:
NSSet *personsFriends=[aPersonvalueForKeyPath:@"friends.friend"];
-
let personsFriends =aPerson.valueForKeyPath("friends.friend")
-
To find out who considers a given person to be their friend, you have to aggregate all the
source
destinations of thebefriendedBy
relationship, for example:NSSet *befriendedByPerson=[aPersonvalueForKeyPath:@"befriendedBy.source"];
-
let befriendedByPerson =aPerson.valueForKeyPath("befriendedBy.source")
Cross-Store Relationships Not Supported
- 注意不要创建从一个持久化存储中的实例到其它持久化存储中的实例的关系,因为core data不支持这种操作。如果你需要创建一个不同存储中的实体间的关系,你需要使用fetched属性。
-
Weak Relationships (Fetched Properties)
-
Fetched properties represent weak, one-way relationships. In the employees and departments domain, a fetched property of a department might be Recent Hires. Employees do not have an inverse to the Recent Hires relationship. In general, fetched properties are best suited to modeling cross-store relationships, loosely coupled relationships, and similar transient groupings.
-
A fetched property is like a relationship, but it differs in several important ways:
-
Rather than being a direct relationship, a fetched property's value is calculated using a fetch request. (The fetch request typically uses a predicate to constrain the result.)
-
A fetched property is represented by an array (
NSArray
), not a set (NSSet
). The fetch request associated with the property can have a sort ordering, and thus the fetched property may be ordered. -
A fetched property is evaluated lazily, and is subsequently cached.
-
-
如果destination实体变了,你必须评估fetched属性来确保他是最新的。使用
refreshObject:mergeChanges:
方法来手动刷新属性,这使得这个fetch request属性当下次对象被访问时执行。 -
To understand how the variables work, consider a fetched property with a destination entity Author and a predicate of the form,
(university.name LIKE [c] $FETCH_SOURCE.searchTerm) AND (favoriteColor LIKE [c] $FETCHED_PROPERTY.userInfo.color)
. If the source object had an attributesearchTerm
equal to Cambridge, and the fetched property had a user info dictionary with a key color and value Green, then the resulting predicate would be(university.name LIKE [c] "Cambridge") AND (favoriteColor LIKE [c] "Green")
. The fetched property would match any Authors at Cambridge whose favorite color is green. If you changed the value of searchTerm in the source object to Durham, then the predicate would be(university.name LIKE [c] "Durham") AND (favoriteColor LIKE [c] "Green")
.The most significant constraint for fetched properties is that you cannot use substitutions to change the structure of the predicate — for example, you cannot change a LIKE predicate to a compound predicate, nor can you change the operator (in this example,
LIKE [c]
)