Core Data: Relationship 总结

本文部分内容来自 Objc.io 的《Core Data》一书,买来一个月后觉得39美元总体还是花得值得的,推荐购买。

Fetch requests 并非获取 managed objects 的唯一途径,而且,应该尽可能避免 fetch。因为 fetch 操作会遍历整个 Core Data Stack,代价很大,是重大的性能瓶颈。获取 managed objects 的另外一个重要途径是 relationship。

Creating Relationships

一般关系

Relationship 有三种:一对一(to-one),一对多(to-many),多对多(many-many)。多对多关系的对应关系也应该是多对多关系。建立关系时尽量避免单向关系,这样不利于 Core Data 维护对象之间的关系。在 Model Editor 里设置关系时注意设置逆向关系 Inverse Relationship,这样 Core Data 可以替我们完成很多工作。

部门 Department 和职员 Employee 的关系设置:


Relationship of Department and Employee

这里部门与职员的关系是一对多,职员与部门的关系是一对一。
一对一关系和 managed object 的其他属性没有多大区别,Xcode 生成的子类里该关系的属性类型就是关系目标的类型;一对多关系里,为了保持维护的对象唯一,子类使用 Set 来维护对对象的引用。若勾选了 Ordered,则使用 NSOrderdSet 类。使用有序关系并没有真的对里面的对象进行排序,只是方便了索引。

extension Department {
    @NSManaged var name: String?
    @NSManaged var employees: NSOrderedSet?
}
extension Employee {
    @NSManaged var name: String?
    @NSManaged var department: Department?
}
不一般关系⊙▂⊙

上面的例子里关系目标都是其他实体 Entity,关系也可以指向自身类型的 Entity,比如,利用 Person 建立一个族谱,那么关系的目标对象都是 Person。这里除了关系引用的是自身类型,也没有什么特别的了。
还有一种比较特别:单向关系。上面也提到了,尽量不要建立单向关系。因为在单向关系里,一方被删除了的话,另一方无法得知。使用单向关系时必须小心。
PS: Core Data 不支持跨 Store 的关系。

Accessing and Manipulating Relationships

访问关系

访问关系有两种途径,一种是和访问普通的对象属性一样:

 let employees: NSOrderedSet = aDepartment.employees
 let department: Department = anEmployee.department

另外一种是使用 KVC 方法,如果传递的 key 不是 Modal 里定义的属性,将会抛出异常:

let employees = aDepartment.valueForKey("employees") as? NSOrderedSet
let department = anEmployee.valueForKey("department") as? Department

除此之外,关系还支持 keypath 访问,path 支持普通的属性 perporty 和关系 relationship:

let departmentName = anEmployee.valueForKeyPath("department.name") as? String
修改关系

修改关系这件事需要好好说明一下:我们只需要修改关系一方,Core Data 会自动替我们处理好剩下的事情。比如下面的情况:


transfer to new department

只需要:

//方法1:
anEmployee.department = newDepartment
//方法2:
anEmployee.setValue(newDepartment, forKey:"department")

或者:

//如果没有勾选 Ordered 选项,使用 mutableSetValueForKey(_:)
newDepartment.mutableSetValueForKey("employees").addObject(employee)
//如果勾选了,使用 mutableOrderedSetValueForKey(_:)
newDepartment.mutableOrderedSetValueForKey("employees").addObject(employee)

只需要使用上面的一种方法就可以了。
如果像批量更改部门的职员构成怎么办,单个移除以及添加很麻烦,使用 KVC 方法。

newDepartment.setValue(newEmployees, forKey:"employees")

在 Department 这一端,因为直接访问employees得到的一个无法更改的量,只能使用mutableSetValueForKey(_:)mutableOrderedSetValueForKey(_:)来进行个体的修改,或者使用setValue(_, forKey:)来进行整体的修改。处于性能的原因,set<Key>:这类方法比如setEmployees:不能用来修改关系。
NSManagedObject重写了valueForKey:setValue:forKey:以及mutableSetValueForKey(_:)这三个 KVC 方法,当 key 不是在 modal 里定义的属性时,这三个方法都会抛出异常。你不应该在子类中重写这三个方法。

Delete Rule

上面只需要在修改一端修改关系剩下的事情由 Core Data 替我们处理了得益于 Delete Rule 的设计。删除规则决定了删除对象时它的关系怎么处理的行为。Core Data 提供了四种删除规则,下面还是用部门与员工之间的关系来举例:

  1. 拒绝 Deny
    如果关系目标里还有对象,比如要删除(撤销)某个部门,但该部门还有一个员工,那么是无法删除(撤销)该部门的,只有该部门里所有的员工被调往其他部门或是被删除(解雇)了才能删除(撤销)该部门。
  2. 失效 Nullify
    移除对象之间的关系但是不删除对象。只有当一方关系是可有可无的时候才有意义,比如员工有没有部门都无所谓,那么删除该对象时只会将其关系设置为空而不会删除对象本身。
  3. 连坐 Cascade
    在这种规则下,可以把一个部门一锅端。删除(撤销)某部门,部门里的员工也会被全部解雇(删除)。
  4. 不作为 No Action
    什么也不做。部门被删除(撤销)后,里面的员工还不知道,以为自己还在这个部门工作呢。
    前三种删除规则还是比较清晰的,都有合适的使用场景,而最后一种比较危险,需要你自己来确定关系里的对象是否还存在。

Relationship Faults

访问 managed object 的 relationship property 时,relationship 对应的 managed object 如果在 context 中不存在,那么被 fetch 进内存时会处于 faults 状态,即使已经存在也不会主动填充 managed object 中的数据,无论原来的 managed object 处于 faults 状态还是已经填充了全部数据。而且,无论是 to-one relationship 还是 to-mant relationship都是这样。这个特性对于维持较低的内存占用具有重要意义。
Relationship faults 有两层:访问 relationship 时,这时候 Core Data 做的仅仅是根据 relationship 的 objectID 来获取相应的 managed object,并不会填充数据;访问 relationship 上的某个属性时,relationship 才会填充该属性对应的数据。

Reference Cycles

关系一般都是双向的,而且关系并不想其他对象一样有强引用和弱引用的区别,在这种情况下,当关系的双方都在内存中后,自然而然就形成了引用循环。打破引用循环的唯一方法是刷新关系中的一方,使用 context 的refreshObject(_:mergeChanges:)来刷新对象。

context.refreshObject(managedObejct, mergeChanges:false)

参数mergeChanges为 false 时,对象会重新进入 faults 状态,这会移除对象里所有的数据,包括与其他对象之间的关系。需要注意的是,在这个参数配置下,该方法会放弃对象身上所有没有保存的变化。
至于何时打破引用循环这取决于应用自身的需要。比如,将当前的 ViewController 从 stack 中移除,你不再需要这些数据了,可以将对象转变为 faults状态;或者应用进入后台,你也可以这样做降低内存占用避免被系统杀掉。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容