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狀態;或者應用進入后臺,你也可以這樣做降低內存占用避免被系統殺掉。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容