Core data 是 Cocoa 中處理數據,綁定數據的關鍵特性,其重要性不言而喻,但也比較複雜。Core Data相關的類比較多,初學者往往不太容易弄懂。我近期花了一週多時間,才基本入門,今天將一些要點整理出來分享:
一、Core Data 框架的概述
Core Data 是 Cocoa 裏面一套非常受歡迎的框架,從 Mac OS X 10.4 提供以來,在 10.5 中引入了完善的schema 遷移機制,再到 iPhone OS 3.0 時被引入 Cocoa Touch,這套完善的框架都被認爲是管理大量結構化數據所首選的 Cocoa 框架,尤其是因爲使用 Core Data 能大大減少需要手工編寫的代碼量,就使它更受開發者歡迎了。
不過最近卻出現了一些不同的聲音,先是傳出消息說 Aperture 3.0 拋棄了 Core Data ,改爲直接操作 SQLite數據庫 (大家聯想到 Apple Mail 3.0 也是直接用 SQLite,沒有用 Core Data),但因爲都是 Apple 內部的決定,大家只能憑空猜測理由;接下來,NetNewsWire 的開發者 Brent Simmons 也說在 NetNewsWire for iPhone 裏從 Core Data 轉向用 FMDB 來操作 SQLite 數據庫 (FMDB 是 Gus Mueller 編寫的一層很薄的 SQLite 在 Objective-C 下的封裝),Brent 給的理由就很充分了:他要做的很多操作都是對數據批量進行的,其實不需要把所有數據都保存在內存裏遍歷執行,那樣更慢,直接交給數據庫,往往一條語句就搞定了,簡潔而且快速。
然後 Jonathan “Wolf” Rentzsch 也對此深表贊同 ,並推薦 Aaron Hillegass 的 BNRPersistence 框架,這個框架用Tokyo Cabinet 提供了一個類似 Core Data 接口的數據持久化方案,最大的優點是比 Core Data 快得多,根據Aaron 自己的測試,常見的操作都要快 10 - 20 倍。其實快這麼多也可以理解,畢竟 BNRPersistence 要比 Core Data 輕量得多,支持的功能也少很多,加上 Tokyo Cabinet 這樣的 Key-value 數據庫在處理適合它的操作時,多數要比 SQLite 這樣的關係型數據庫要快。
所以突然 Core Data 就有點被牆倒衆人推的意思,好像以前大家都知道它不好用,但都不好意思說,直到突然有經驗足夠豐富的開發者開頭,就一涌而上開始罵了。我個人的觀感是 Core Data 作爲官方方案,給開發提供的許多便利還是不可小視的,但考慮學起來確實也不容易 (所以纔有人專門寫本書講 Core Data ),所以新上手的Cocoa 程序員不妨先考慮一下 BNRPersistence, FMDB 這樣的方案。
那是否 Core Data 就該被拋棄呢?目前的爭議其實有點像 Web 開發裏到底該不該用 ORM , 區別是大多數Web 開發者因爲歷史原因,對直接進行數據庫操作有偏好 (或者說,本能地反感 ORM),而多數 Cocoa 開發者則堅定支持用對象操作數據,所以長遠來看,數據持久化方案在 Cocoa (Touch) 開發裏少不了,唯一的疑問是 Apple會不會繼續改進 Core Data 的性能和接口,以拉近與第三方方案的差距,只要 Apple 還在不斷改進,Core Data 就有學習的必要。
我個人對 Core Data 的怨念主要是在要寫的“膠水代碼”上:有的時候爲了方便與界面的 bindings 操作,不得不給 Model 寫大量重複的膠水代碼,所以如果第三方的方案如果在這方面有簡化,我很樂意改用。
二、Core Data與SQLite數據庫
並非嚴格的說, CoreData是對SQLite數據庫的一個封裝。
在蘋果iPhone或iPad上,你可以存放數據在以下三個地方
其一是:一個或多個文件
其二是:應用的屬性列表(NSUserDefaults)
其三是:內置的數據庫(SQLite)
通常SQLite數據庫操作的基本流程是, 創建數據庫, 再通過定義一些字段來定義表格結構, 可以利用sql語句向表格中插入記錄, 刪除記錄, 修改記錄, 表格之間也可以建立聯繫。
這個過程出現了, 表格的結構(schema), 所有表格的結構和相互聯繫構成整個數據庫的模型, 數據庫存放的方式(可以是文件或者在內存), 數據庫操作, sql語句(主要是查詢), 表格裏面的記錄,下面將上面說的文字,跟CoreData的類作個對應:
SQLite數據庫操作 |
VS |
CoreData的類 |
表格結構 |
--> |
NSEntityDescription |
數據庫中所有表格和他們的聯繫 |
--> |
NSManagedObjectModel |
數據庫存放方式 |
--> |
NSPersistentStoreCoordin |
數據庫操作 |
--> |
NSManagedObjectContext |
查詢語句 |
--> |
NSFetchRequest |
表格的記錄 |
--> |
NSManagedObject |
可能上面的對應關係並非十分嚴格, 但確實可以幫助理解.
下面再看看CoreData的類
首先是NSEntityDescription和NSManagedObjectModel這兩個非常重要的類
NSEntityDescription用來定義表格結構,所以你就可以理解NSManagedObjectModel中的setEntities:(NSArray *)entities函數大概有什麼用了,通常, 定義model,是用文件CoreData.xcdatamodel, 可以圖形化的操作. 這類似用nib來創建界面。
例如:建個工程, 使用core
用命令行sqlite3 CoreData.sqlite 進入
>.tables
ZEVENT
可以看到有表格ZEVENT, 對應的CoreData.xcdatamodel文件有名字叫Event的Entity
>.schema ZEVENT
CREATE TABLE ZEVENT ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZTIMESTAMP TIMESTAMP );
對應的Event中有屬性timeStamp, 可以看到, 相應的ZEVENT表格中有字段TIMESTAMP
> select * from ZEVENT
1|1|1|306295807.974966
2|1|1|306295810.981875
3|1|1|306295811.982537
這表格有三個記錄, 可以用來初始化三個NSManagedObject, 修改了NSManagedObject, save之後也修改了表格記錄。
你可以在CoreData.xcdatamodel添加新的entity, 之後用sqlit3命令來查看數據庫的變化。
三、深入認識Core Data 框架
下面先給出一張類關係圖,讓我們對它有個總體的認識。
在上圖中,我們可以看到有五個相關模塊:
Managed Object Context 參與對數據對象進行各種操作的全過程,並監測數據對象的變化,以提供對undo/redo 的支持及更新綁定到數據的 UI。使用Core Data的框架,大多數的功能都可以自動實現,因爲我們有managed object context(管理對象的上下文,有時直接叫"Context")。managed object context就像是一個關卡,通過它可以訪問框架底層的對象——這些對象的集合我們稱之爲"persistence stack"(數據持久棧)。 managed object context作爲程序中對象和外部的數據存儲的中轉站。棧的底部是persistence object stores(持久化數據存儲)
Persistent Store Coordinator 相當於數據文件管理器,處理底層的對數據文件的讀取與寫入。一般我們無需與它打交道。之前提到過,程序中的對象和外部存儲的數據通過Core Data框架中的一系列對象進行協調,這一系列的對象總的被稱爲持久存儲棧(Persistence stack)。在棧頂是被管理對象上下文(Managed object context),而棧底是持久化對象存儲層(Persistence object store)。在它們之間就是持久化存儲助理。
這些模塊是怎樣運作的呢?
1、應用程序先創建或讀取模型文件(後綴爲xcdatamodeld)生成 NSManagedObjectModel 對象。Document應用程序是一般是通過 NSDocument 或其子類 NSPersistentDocument)從模型文件(後綴爲xcdatamodeld)讀取。
2、然後生成 NSManagedObjectContext 和 NSPersistentStoreCoordin
3、NSPersistentStoreCoordin
4、NSManagedObjectContext 參與對數據進行各種操作的整個過程,它持有 Managed Object。我們通過它來監測 Managed Object。監測數據對象有兩個作用:支持 undo/redo 以及數據綁定。這個類是最常被用到的。
5、Array Controller, Object Controller, Tree Controller 這些控制器一般與NSManagedObjectContext 關聯,因此我們可以通過它們在 nib 中可視化地操作數據對象。
四、Model class
模型有點像數據庫的表結構,裏面包含 Entry, 實體又包含三種 Property:Attribute(屬性),RelationShip(關係), Fetched Property(讀取屬性)。Model class 的名字多以 "Description" 結尾。我們可以看出:模型就是描述數據類型以及其關係的。
主要的 Model class 有:
Model Classes |
||
Managed Object Model |
NSManagedObjectModel |
數據模型 |
Entity |
NSEntityDescription |
抽象數據類型,相當於數據庫中的表 |
Property |
NSPropertyDescription |
Entity 特性,相當於數據庫表中的一列 |
|
NSAttributeDescription |
基本數值型屬性(如Int16, BOOL, Date等類型的屬性) |
|
NSRelationshipDescriptio |
屬性之間的關係 |
|
NSFetchedPropertyDescrip |
查詢屬性(相當於數據庫中的查詢語句) |
1)Entity - NSEntityDescription
Entity 相當於數據庫中的一個表,它描述一種抽象數據類型,其對應的類爲 NSManagedObject 或其子類。
NSEntityDescription 常用方法:
+insertNewObjectForEntity
-managedObjectClassName //返回映射到 Entity 的 NSManagedObject 類名
-attributesByName //以名字爲 key, 返回 Entity 中對應的 Attributes
-relationshipsByName //以名字爲 key, 返回 Entity 中對應的 Relationships
2)Property – NSPropertyDescription
Property 爲 Entity 的特性,它相當於數據庫表中的一列,或者 XML 文件中的 value-key 對中的 key。它可以描述實體數據屬性(Attribute),Entity之間的關係(RelationShip),或查詢屬性(Fetched Property)。
Attribute 存儲基本數據,如 NSString, NSNumber or NSDate 等。它可以有默認值,也可以使用正則表達式或其他條件對其值進行限定。一個屬性可以是 optional 的。
Relationship 描述 Entity,Property 之間的關係,可以是一對一,也可以是一對多的關係。
Fetched Property 根據查詢謂詞返回指定 Entity 的符合條件的數據對象。
上面說的比較抽象,舉個例子來說,見圖:
我們通常使用 KVC 機制來訪問 Property。下面來看代碼:
[代碼]c#/cpp/oc代碼:
1 |
NSManagedObjectContext
* context = [[NSApp delegate ]
managedObjectContext]; |
2 |
NSManagedObject
* author = nil; |
3 |
|
4 |
author
= [NSEntityDescription insertNewObjectForEntityForName: @"Author" inManagedObjectContext:
context]; |
5 |
[author
setValue: @"[email protected]" forKey: @"email" ]; |
6 |
7 |
NSLog
( @"The
Author's email is: %@" ,
[author valueForKey: @"email" ]); |
在上面代碼中,我們先取得 NSManagedObjectContext,然後調用 NSEntityDescription 的方法,以 Author 爲實體模型,生成對應的 NSManagedObject 對象,插入 NSManagedObjectContext 中,然後給這個對象設置特性 email的值。
> Managed Object – NSManagedObject
Managed Object 表示數據文件中的一條記錄,每一個 Managed Object 在內存中對應 Entity 的一個數據表示。Managed Object 的成員爲 Entity 的 Property 所描述。
比如在上面的代碼,author 這個 NSManagedObject,對應名爲 Author 的 Entity。
每一個 Managed Object 都有一個全局 ID(類型爲:NSManagedObjectID)。Managed Object 會附加到一個Managed Object Context,我們可以通過這個全局 ID 在 Managed Object Context 查詢對應的 Managed Object。
NSManagedObject 常用方法 |
|
-entity |
獲取其 Entity |
-objectID |
獲取其 Managed Object ID |
-valueForKey: |
獲取指定 Property 的值 |
-setValue: forKey: |
設定指定 Property 的值 |
> Managed Object Context – NSManagedObjectContext
Managed Object Context 的作用相當重要,對數據對象進行的操作都與它有關。當創建一個數據對象並插入Managed Object Context 中,Managed Object Context 就開始跟蹤這個數據對象的一切變動,並在合適的時候提供對 undo/redo 的支持,或調用 Persistent Store Coordinato 將變化保存到數據文件中去。
通常我們將 controller 類(如:NSArrayController,NSTreeController)或其子類與 Managed Object Context 綁定,(如何綁定?後有介紹)這樣就方便我們動態地生成,獲取數據對象等。
NSManagedObjectContext 常用方法 |
|
-save: |
將數據對象保存到數據文件 |
-objectWithID: |
查詢指定 Managed Object ID 的數據對象 |
-deleteObject: |
將一個數據對象標記爲刪除,但是要等到 Context 提交更改時才真正刪除數據對象 |
-undo |
回滾最後一步操作,這是對undo/redo 的支持 |
-lock |
加鎖,常用於多線程以及創建事務。同類接口還有:-unlock and -tryLock |
-rollback |
還原數據文件內容 |
-reset |
清除緩存的 Managed Objects。只應當在添加或刪除 Persistent Stores 時使用 |
-undoManager |
返回當前 Context 所使用的 NSUndoManager |
-assignObject: toPersistantStore: |
由於 Context 可以管理從不同數據文件而來的數據對象,這個接口的作用就是指定數據對象的存儲數據文件(通過指定 PersistantStore 實現) |
-executeFetchRequest: error: |
執行 Fetch Request 並返回所有匹配的數據對象 |
> Persistent Store Coordinator -
NSPersistentStoreCoordin
使用 Core Data document 類型的應用程序,通常會從磁盤上的數據文中中讀取或存儲數據,這寫底層的讀寫就由 Persistent Store Coordinator 來處理。一般我們無需與它直接打交道來讀寫文件,Managed Object Context 在背後已經爲我們調用 Persistent Store Coordinator 做了這部分工作。
NSPersistentStoreCoordin |
|
-addPersistentStoreForURL |
裝載數據存儲,對應的卸載數據存儲的接口爲 -removePersistentStore:error: |
-migratePersistentStore:toURL:options:withType:error: |
遷移數據存儲,效果與 "save as"相似,但是操作成功後,遷移前的數據存儲不可再使用 |
-managedObjectIDForURIRep |
返回給定 URL所指示的數據存儲的 object id,如果找不到匹配的數據存儲則返回 nil |
-persistentStoreForURL: |
返回指定路徑的 Persistent Store |
-URLForPersistentStore: |
返回指定 Persistent Store 的存儲路徑 |
> Persistent Document - NSPersistentDocument
NSPersistentDocument 是 NSDocument 的子類。 multi-document Core Data 應用程序使用它來簡化對Core Data 的操作。通常使用 NSPersistentDocument 的默認實現就足夠了,它從 Info.plist 中讀取Document types 信息來決定數據的存儲格式(xml,sqlite, binary)。
NSPersistentDocument 常用方法 |
|
-managedObjectContext |
返回文檔的 Managed Object Context,在多文檔應用程序中,每個文檔都有自己的 Context。 |
-managedObjectModel |
返回文檔的 Managed Object Model |
Fetch Requests 相當於一個查詢語句,你必須指定要查詢的 Entity。我們通過 Fetch Requests 向Managed Object Context 查詢符合條件的數據對象,以 NSArray 形式返回查詢結果,如果我們沒有設置任何查詢條件,則返回該 Entity 的所有數據對象。我們可以使用謂詞來設置查詢條件,通常會將常用的 Fetch Requests 保存到 dictionary 以重複利用。
NSFetchRequest 常用方法 |
|
-setEntity: |
設置你要查詢的數據對象的類型(Entity) |
-setPredicate: |
設置查詢條件 |
-setFetchLimit: |
設置最大查詢對象數目 |
-setSortDescriptors: |
設置查詢結果的排序方法 |
-setAffectedStores: |
設置可以在哪些數據存儲中查詢 |
[代碼]c#/cpp/oc代碼:
01 |
NSManagedObjectContext
* context = [[NSApp delegate ]
managedObjectContext]; |
02 |
NSManagedObjectModel
* model = [[NSApp delegate ]
managedObjectModel]; |
03 |
NSDictionary
* entities = [model entitiesByName]; |
04 |
NSEntityDescription
* entity = [entities valueForKey: @"Post" ]; |
05 |
|
06 |
NSPredicate
* predicate; |
07 |
predicate
= [NSPredicate predicateWithFormat: @"creationDate
> %@" ,
date]; |
08 |
|
09 |
NSSortDescriptor
* sort = [[NSortDescriptor alloc] initWithKey: @"title" ]; |
10 |
NSArray
* sortDescriptors = [NSArray arrayWithObject: sort]; |
11 |
|
12 |
NSFetchRequest
* fetch = [[NSFetchRequest alloc] init]; |
13 |
[fetch
setEntity: entity]; //設置你要查詢的數據對象的類型(Entity) |
14 |
[fetch
setPredicate: predicate]; //設置查詢條件 |
15 |
[fetch
setSortDescriptors: sortDescriptors]; //
設置查詢結果的排序方法 |
16 |
|
17 |
NSArray
* results = [context executeFetchRequest:fetch error:nil]; |
18 |
[sort
release]; |
19 |
[fetch
release]; |
在上面代碼中,我們查詢在指定日期之後創建的 post,並將查詢結果按照 title 排序返回。
參考資料:
Core Data Reference API listing for the Core Data classes
http://developer.apple.com/documentation/Cocoa/Reference/CoreData_ObjC/index.html
NSPredicate Reference API listing for NSPredicate
http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSPredicate.html
原貼地址:http://www.devdiv.com/iPhone開發學習筆記——Core_Data_框架及運作過程和設計的類-weblog-228548-49362.html