简译 Relationships and Fetched Properties

本文探讨了CoreData中实体之间的关系管理,包括一对一、一对多及多对多关系的定义与应用,重点讲解了如何处理关系的删除规则以及如何维护对象图的一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Relationships and Fetched Properties

创建relationship时,需要考虑很多事情。

1.目标实体是哪个?

2.对一关系还是对多关系?

3.如果是对多关系,关系中最多和最少的数目为多少?

4.源对象删除时会发生些什么?

CoreData使(你改变managed objects间关系而不引起参照完整性问题)变得简单。

CoreData 不允许跨存储(文件)创建关系,如果有这样的需求,可以考虑使用fetched properties;

在managed object model中创建关系是简单的。但要考虑关系的很多方面,eg:关系的名字、目标实体、基数(对一 对多),最重要的是反向关系和删除原则。

Relationship Fundamentals

一对一关系表示为一个目标对象的引用。一对多关系表示为多个对象的集合的引用。

多对多关系 :正向和反向关系都为一对多关系。

一对多关系中,可以设置目标的上限和下限(个数)。比如一个部门的人数必须为3-40人。

可以指定关系是不是可选的

尽管你设置的关系的上限和或下限,也可以设置关系为可选的。就是说不必有任何目标对象,但是如果有就必须满足上限或下限的个数范围。

创建一个源对象并不会因为定义了简单关系而创建目标对象。

建立关系类似于声明一个成员变量。

@interface Widget : NSObject

{

    Sprocket *sprocket;

}

如果创建一个Widget实例,并不意味着创建了一个Sprocket。类似的,如果你创建了一个非可选的从Employee到Address的关系,创建Employee实例并不

会创建一个新的Address实例。

Inverse Relationships

绝大多数关系天生是双向性的。主要的例外是Fetched property,表示弱的单向关系。

Relationship Delete Rules

当企图删除源对象时,关系的删除规则指定了将会发生什么。请注意是:如果试图…… 当删除规则设置为Deny,源对象很可能不会被删除。下面通过从Department 到 Employee 的关系阐述删除规则的影响:

Deny 拒绝

如果有至少一个目标对象,那么源对象不会被删除。如果你想删除department,你必须确保此部门中所有employees被转移到其他地方或者被解雇,否则这个department不会被删除。

Nullify 作废

设置目标对象反向关系的目标为null

如果删除一个department,那么将此department中雇员的department设置为null。仅仅在从Employee到Department的关系为可选时起作用,或者你确定在调用存储前,为每一个employee设置了新的department。

Cascade 级联删除

删除关系的所有目标对象。如果删除一个部门,那么就同时解雇这个部门的所有雇员。

No Action 神马都不做

对关系的目标对象不做任何事。删除一个部门的时候,不对此部门中的雇员做任何事,他们还会认为他们属于原来的那个部门。

前三个规则在不同的环境下是有用的,对于任何关系来说,选择合适的规则取决于逻辑。No Action 很少被使用,如果你使用这个规则,那么你得负责维护对象图的一致性。你得为所有目标对象的反向关系设置有意义的值。这样也许是有好处的,比如有一个一对多关系,有大量的目标对象。

Manipulating Relationships and Object Graph Integrity

总的来说,以编程的方式操作关系是很简单的。怎么做请看 “Accessing and Modifying Properties”

因为CoreData 照顾对象图一致性操作,你只需要改变关系的一端,其他方面都为你管理好了。

从雇员到管理者的关系暗示着一个反向关系:从管理者到雇员的关系。如果一个新的雇员被分配到指定的管理者,管理者应该明白他的责任,新的雇员必须增加到管理者报告的名单中。如果一个雇员从一个部门转移到另一个部门,也需要做一系列的处理。这个雇员要更新他的部门,之前部门的雇员列表删除这个雇员,新部门的雇员列表增加这个雇员。如下图:


如果没有CoreDataFramework 你必须写几行代码确保对象图的一致性。而且,你必须熟悉Department的实现以了解是否应该设置反向关系。使用CoreDataFramework 一句话搞定:

anEmployee.department = newDepartment;

或者使用

[newDepartment addEmployeeObject:anEmployee];


Many-to-Many Relationships

两个一对多关系,相互为反向关系,构成多对多关系。(如果你有数据库管理背景,可能引起你的担心,不必担心,CoreData自动创建中间表)

源对象和目标对象为同一个实体也是可以的。成为反身关系。如雇员有多个直接汇报的管理者,管理者有多个直接向他汇报的雇员。定义了从Employee到它自己的directReport多对多关系。


一个关系同样能将它自己设置为它的反向关系。如cousins关系


你应该考虑关系的语义和怎么建模。比如friends的多对多关系,你应该使用一个中间的实体。这样做你还可以给这个关系增加更多的信息,如FriendInfo实体能够包含友情强度的指标。


在这个例子中,Person有两个一对多关系到FriendInfo。friends表示源对象的朋友们,befriendedBy 表示把源对象当做朋友的人们。FriendInfo表示单向友情的信息。单向哦。FriendInfo实例表明谁是源和被源当做朋友的一个人。如果这感觉是相互的,那么将会有一个相应的FriendInfo实例,它的source和friend是互换的。这种情况下,我们要考虑如下因素:

⚫️为了建立从一个人到另一个人的朋友关系,你应该创建一个FriendInfo实例,如果两个人彼此成为朋友,那么你应该创建两个FriendInfo实例。

⚫️打断一个朋友关系,你必须删除恰当的FriendInfo实例。

⚫️从Person到FriendInfo的删除规则应该是级联删除。当把一个人从内存中删除,那么跟这个人相关的FriendInfo实例们不可用了,所以必须删除掉。因此推论从FriendInfo到Person的关系一定不是可选的。source 或者friend之一是null那么FriendInfo实例就是无效的。

⚫️为了找出一个人的朋友们都是谁,你得找出(所有friends所指向FriendInfo实例)的friend。一个人有多个friends 对应着多个FriendInfo实例,这多个实例的friend就是这个人的朋友。

可以方便的使用一句话:NSSet *personsFriends = [aPerson valueForKeyPath:@"friends.friend"];

为了方便的找出都有谁把指定的人当做朋友,你得找出(所有befriendedBy所指向FriendInfo实例)的source

NSSet *befriendedByPerson = [aPerson valueForKeyPath:@"befriendedBy.source"];


Unidirectional Relationships

设计一个双向关系不是完全必要的。有些时候非双向关系是有用的,如当一对多关系有大量目标对象,并且你很少能反向这个关系。创建非双向关系给你增加很多责任来确保对象图的一致性。由于这个原因,这种做法是被强烈劝阻的。通常仅仅在对一关系中使用单向关系。

创建单向关系的model时,对象图可能产生不一致的状态。

接下来的例子阐明了只创建单向关系可能引起的问题。有两个实体:Employee和Department存在一个从Employee到Department的对一关系,这个关系是非可选的并且删除原则为Deny。

Employee *employee;

Department *department;

// assume entity instances correctly instantiated

[employee setDepartment:department];

[managedObjectContext deleteObject:department];

BOOL saved = [managedObjectContext save:&error];

因为employee没有以其他方式发生改变,所以成功存储。由于是单向关系,在department被删除时employee没有被标记为改变的。如果你添加上如下代码:

id x = [employee department];

x错误的设值为“nowhere”而不是nil


如果存在反向关系,删除规则不是No Action。删除department时employee被标记为改变的。

以上阐述了尽量避免使用单向关系的原因,如果你确实想使用单向关系,那么建议在删除department后增加一句;

[managedObjectContext deleteObject:department];

[employee setValue:nil forKey:@"department”];

接下来调用save的时候就会报错了,因为是非可选的。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值