> 原文出處: http://chengway.in/post/ji-zhu/core-data-by-tutorials-bi-ji
最近花了半個月讀完了Raywenderlich家的[《Core Data by Tutorials》](http://www.raywenderlich.com/store/core-data-by-tutorials),接下來幾天就做個回顧,筆記并不是對原書的簡單翻譯,算是對整個知識脈絡的一個整理的過程吧:)

**Update:**?上周去搞**Sketch**了,工具算是基本入門了,抄了幾個圖標,畫了幾個圖,以后會再聊聊。好了,進入正題吧,今天打算介紹前三章,也是CoreData的最基礎的部分。***
## **Chapter 1:Your First Core Data App**
第一章比較簡單,主要是帶你熟悉CoreData的用法,并用CoreData創建了一個簡單的List APP,學完這章你能加滿如下技能點:
* model data you want to store in Core Data using Xcode’s model editor;
* add new records to Core Data;
* fetch a set of records from Core Data;
* display the fetched results to the user in a table view.
第一章按順序大致如下:
### **一、Getting started**
作者提供了一個Start Project,打開其實就是一個UITableViewController,你需要做的就是增加一個**addName**方法,這里使用了*UIAlertController*這個iOS 8新添加的方法來做數據的添加邏輯,使用起來也很優雅。
### **二、Modeling your data**
這個主要是根據你的數據模型創建對應的managed object model,主要操作也在*.xcdatamodeld*中進行,全GUI操作,沒什么難度。
> You can think of a Core Data entity as a class “definition” and the managed object as an instance of that class.
> 這里作者用了面向對象的思想做了類比,雖然不是很準確,但也方便初學者理解。
### **三、Saving to Core Data**
這里要注意到NSManagedObject的“對象”其實是遵循**“KVC”**的,NSManagedObject對象的值改變后,在ManagedContext中進行save操作。
### **四、Fetching from Core Data**
創建一個NSFetchRequest對象,設置好各種條件,依舊是交給ManagedContext對象去執行。下面是作者描述了兩種*fetch*失敗的情況
> If there are no objects that match the fetch request’s criteria, the method returns an optional value containing an empty array.
> If an error occurred during the fetch, the method returns an optional value that contains nil. If this happens, you can inspect the NSError and respond appropriately.
**第一章到此結束:)**
* * *
## **Chapter 2:NSManagedObject Subclasses**
NSManagedObject上一章我們提到過,要存取屬性,只能用KVC,使用起來既不安全也不符合面向對象的原則,所以我們這章要生成他的子類,來創建我們自己的屬性,這樣就又能愉快地使用**"."**語法啦。
第二章的大致順序如下:
### **一、Getting started**
作者提供了一個**“挑選領結”**的Start Project,領結數據存放在plist文件中,同樣是一個很簡單的App。
### **二、Modeling your data**
打開xcdatamodeld文件進行編輯,添加屬性。這里主要看一下**Binary Data**與**Transformable**兩種類型:
* **Binary Data**這里將image對象保存成了二進制文件,當然任何可以序列化的對象其實都可以保存成二進制。這里作者強調了性能問題,如果將一個很大的二進制保存到SQLite數據庫中很可能會產生卡頓等問題。當然,幸運的是Core Data提供了一種叫**Allows External Storage**的解決方式,他只對binary data的屬性類型有效,你只需要簡單的開啟他就好了。
> When you enable Allows External Storage, Core Data heuristically decides on a per-value basis if it should save the data directly in the database or store a URI that points to a separate file.
* **Transformable**除了一些基本屬性,如果一個對象遵循**NSCoding Protocol**,那么這個對象是可以選擇使用transformable類型的,作者這里使用的是*UIColor*類型,遵循*NSSecureCoding*協議,而該協議又繼承自*NSCoding*,所以設為Transformable是OK的。自定義的類想要設置為*Transformable*,那么你首先要實現**NSCoding protocol**
### **三、Managed object subclasses**
在Xcode中選擇**Editor\Create NSManagedObject Subclass**創建managedObject對象的子類,這里注意下Map Model中的attribute type與實際子類對象中的屬性的對應關系就好了
* String maps to String
* Integer 16/32/64, Float, Double and Boolean map to NSNumber
* Decimal maps to NSDecimalNumber
* Date maps to NSDate
* Binary data maps to NSData
* Transformable maps to AnyObject
當然如果你想保留Map Model中的原始基本類型,那么在創建**NSManagedObject Subclass**時要勾選*Use scalar properties for primitive data types*
> Similar to @dynamic in Objective-C, the @NSManaged attribute informs the Swift compiler that the backing store and implementation of a property will be provided at runtime instead of at compile time.
> 這里注意下兩種語言的一點區別
創建完子類還要記得一點就是在Model中的**Attributes inspector**將*class name*設為你新生成的子類路徑,完成這一步主要是為了在runtime時將managed object subclass與Model中的entity鏈接起來。(有點類似與在SB中創建一個VC,并設置他的custom class)。
### **四、Propagating a managed context**
因為Xcode默認的CoreData模板會將context在AppDelegate中創建,所以,就會有這種獲取context的做法:
~~~
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
~~~
但這種做法還是不推薦使用,較好的方法是能夠在對象的的初始化中將context作為參數進行傳遞。
接下來,作者從plist中將數據通過子類對象導入到core data中,再對界面做了些操作,這都比較簡單。
### **五、Data validation in Core Data**
數據校驗這里可以在data model inspector 中設置最大最小值,如果出錯會在context save的時候將錯誤信息傳給error參數
**第二章到此結束:)**
* * *
## **Chapter 3:The Core Data Stack**
Core Data要正常運行,需要幾個組件共同協作來完成,這些組件包括:
* NSManagedObjectModel
* NSPersistentStore
* NSPersistentStoreCoordinator
* NSManagedObjectContext
這些組件共同構成了Core Data Stack,組件間的關系我覺得用蘋果官方提供的一張圖來展示最好不過了
> 
>
> * An external persistent store that contains saved records.
> * A persistent object store that maps between records in the store and objects in your application.
> * A persistent store coordinator that aggregates all the stores.
> * A managed object model that describes the entities in the stores.
> * A managed object context that provides a scratch pad for managed objects.
基本Core Data整個操作都是圍繞這張圖來做的,本章作者給出的例子是一個記錄**狗狗?散步**的APP,按照時間順序添加散步記錄。
第三章的大致順序如下:
### **一、The managed object model**
The NSManagedObjectModel 反映了APP中所有的對象類型在data model中所擁有的屬性,以及對象之間的關系。作者還提到了我們用Xcode提供的visual editor創建/編輯了一個*xcdatamodel file*,然而真正在幕后的是一個叫*momc*的編譯器(compiler),把model file編譯后的結果放到**momd**文件夾下。Core Data可以很高效地在運行時使用*momd文件夾里編譯過的內容*,來初始化一個**NSManagedObjectModel**實例。
### **二、The persistent store**
Core Data提供了四種開箱即用的NSPersistentStore存儲類型,三種原子型*atomic*的,一種非原子型*non-atomic*的
> An atomic persistent store needs to be completely deserialized and loaded into memory before you can make any read or write operations. In contrast, a non- atomic persistent store can load chunks of itself onto memory as needed.
* NSQLiteStoreType 依托SQLite數據庫,也是唯一的非原子型的*non-atomic*
* NSXMLStoreType 依托于XML文件,是原子型的*atomic*
* NSBinaryStoreType 依托于二進制文件,是原子型的*atomic*
* NSInMemoryStoreType 其實是存在于**內存中**的*persistent store type*,不算嚴格意義上的持久化存儲,通常用來做單元測試和緩存。
除了上述介紹過的存儲類型,作者說了,只要你的數據類型是基于JSON和CSV的格式,你還可以通過創建**NSIncrementalStore**的子類來創建自己的persistent store類型。
> [Incremental Store Programming Guide](https://developer.apple.com/library/ios/documentation/DataManagement/Conceptual/IncrementalStorePG/Introduction/Introduction.html)
### **三、The persistent store coordinator**
NSPersistentStoreCoordinator可以看成是一座連接managed object model與persistent store的橋梁,他負責理解model,更好地來處理信息的存取。特別是有多個persistent stores時,persistent store coordinator相對managed context提供唯一的接口,保證了context與特定的persistent store交互。
### **四、The managed object context**
之前的章節提到過context可以看做是內存中的scratchpad,來記錄所有的managed object的行為,當然managed object所做的任何改變,在context沒有save()之前,都是不會在數據庫中生效的。
作者又提到了關于context的五個比較重要的特性:
~~~
①. The context 管理著對象們的生命周期,不管這些對象是create還是fetch到的,這種對生命周期的管理在faulting、inverse、relationship handling 和 validation時很有用。
②. A managed object不能脫離context而存在,他們是緊緊地綁在一起的。
③. contexts都很有領土意識?,一旦一個managed object被歸到某個context中去了,那么這個managed object在他整個生命周期內屬于這個context了。
④. 一個應用可以使用多個context,大多非凡的Core Data應用都這么搞。
⑤. A context是非線程安全的,你最好不要跨線程去使用context,Apple提供了多種方式來在多線程中使用context,后面會講到。
~~~
### **五、Creating your stack object**
創建自己的core data stack,其實只要記住本章開始那張圖,記住要創建四個對象以及他們之間的關系,創建起來還是比較簡單的。
~~~
class CoreDataStack {
let context:NSManagedObjectContext
let psc:NSPersistentStoreCoordinator
let model:NSManagedObjectModel
let store:NSPersistentStore?
}
~~~
你要做到工作就是創建這些對象以及他們之間的關系,具體代碼見蘋果官方提供的[Snippet](https://developer.apple.com/library/ios/documentation/DataManagement/Conceptual/CoreDataSnippets/Articles/stack.html)
創建完畢,使用起來也相當簡單,這里注意使用**Lazy**來懶加載
~~~
lazy var coreDataStack = CoreDataStack()
~~~
### **六、Modeling your data**
接下來就是在Xcode里創建model,這里也沒什么難度。增加兩個Entity,設置他們的關系為一對多,并在一對多的關系下面的Arrangement那里勾選Ordered

### **七、Adding managed object subclasses**
這里通過Xcode生成的子類要注意一下,剛才一對多的關系因為上圖勾選了Ordered,所以這里生成了一個NSOrderedSet類型的對象。*一開始我“以為這個真是一個有序集合,CoreData會為這個集合里的子對象們排序”*,但經過和[@Kyrrr](http://www.weibo.com/u/2626996387)同學討論試驗,發現并不是這么一回事。真正的原因是**集合里的子對象們都是無序的,本來應該用數組。但數組保證不了子對象的唯一性,所以才用了集合。而使用有序集合只是為了將來使用起來索引方便,而并不是排序。**作者下面也有提到:
> NSSet seems like an odd choice, doesn’t it? Unlike arrays, sets don’t allow accessing their members by index. In fact, there’s no ordering at all! Core Data uses NSSet because a set forces uniqueness in its members. The same object can’t feature more than once in a to-many relationship.
這里同樣要注意的一點,還是創建完子類記得去model對應的Entity那里,Data Model inspector下填寫相應的Class路徑。
### **八、A walk down persistence lane**
接著就去ViewController做一些工作來使用CoreData,完成狗狗散步的App,這一步也比較簡單。
### **九、Deleting objects from Core Data**
刪除一條記錄,先刪除context中的相應object,保存后,從UI中刪去相關條目。