Core Data

From:http://hxsdit.com/1622   

Core Data是一個Cocoa框架,用於爲管理對象圖提供基礎實現,以及爲多種文件格式的持久化提供支持。管理對象圖包含的工作如撤銷(undo)和重做(redo)、有效性檢查、以及保證對象關係的完整性等。對象的持久化意味着Core Data可以將模型對象保存到持久化存儲中,並在需要的時候將它們取出。Core Data應用程序的持久化存儲(也就是對象數據的最終歸檔形式)的範圍可以從XML文件到SQL數據庫。Core Data用在關係數據庫的前端應用程序是很理想的,但是所有的Cocoa應用程序都可以利用它的能力。

         Core Data的核心概念是託管對象。託管對象是由Core Data管理的簡單模型對象,但必須是NSManagedObject類或其子類的實例。可以用一個稱爲託管對象模型的結構(schema)來描述Core Data應用程序的託管對象(Xcode中包含一個數據建模工具,可以幫助您創建這些結構)。託管對象模型包含一些應用程序託管對象(也稱爲實體)的描述。每個描述負責指定一個實體的屬性、它與其它實體的關係、以及像實體名稱和實體表示類這樣的元數據。

         在一個運行着的Core Data程序中,有一個稱爲託管對象上下文的對象負責管理託管對象圖。圖中所有的託管對象都需要通過託管對象上下文來註冊。該上下文對象允許在圖中加入或刪除對象,以及跟蹤圖中對象的變化,並因此可以提供撤銷(undo)和重做(redo)的支持。當準備好保存對託管對象所做的修改時,託管對象上下文負責確保那些對象處於正確的狀態。當Core Data應用程序希望從外部的數據存儲中取出數據時,就向託管對象上下文發出一個取出請求,也就是一個指定一組條件的對象。在自動註冊之後,上下文對象會從存儲中返回與請求相匹配的對象。

         託管對象上下文還作爲訪問潛在Core Data對象集合的網關,這個集合稱爲持久化堆棧。持久化堆棧處於應用程序對象和外部數據存儲之間,由兩種不同類型的對象組成,即持久化存儲和持久化存儲協調器對象。持久化存儲位於棧的底部,負責外部存儲(比如XML文件)的數據和託管對象上下文的相應對象之間的映射,但是它們不直接和託管對象上下文進行交互。在棧的持久化存儲上面是持久化存儲協調器,這種對象爲一或多個託管對象上下文提供一個訪問接口,使其下層的多個持久化存儲可以表現爲單一一個聚合存儲。圖7-1顯示了Core Data架構中各種對象之間的關係。

 

圖7-1

         Core Data中包含一個NSPersistentDocument類,它是NSDocument的子類,用與協助Core Data和文檔架構之間的集成。持久化文檔對象創建自己的持久化堆棧和託管對象上下文,將文檔映射到一個外部的數據存儲;NSPersistentDocument對象則爲NSDocument中讀寫文檔數據的方法提供缺省的實現。

         通過Core Data管理應用程序的數據模型,可以極大程度減少需編寫的代碼數量。Core Data還具有下述特徵:

  1. 將對象數據存儲在SQLite數據庫以獲得性能優化。
  2. 提供NSFetchedResultsController 類用於管理表視圖的數據。即將Core Data的持久化存儲顯示在表視圖中,並對這些數據進行管理:增、刪,改。
  3. 管理undo/redo操作。
  4. 檢查託管對象的屬性值是否正確。

 

7.1.1.           Core Data基本架構

         在大多數應用中,需要打開一個文件,此文件包含一個有多個對象的歸檔,或者包含一個對至少一個根對象的引用。還需要能夠歸檔所有對象到一個文件,並跟蹤對象的變化。例如,在一個員工管理應用中,需要一種方法來打開一個文件,其中包含員工和部門對象的歸檔,幷包含一個對至少一個根對象的引用——例如,含有全體員工的數組——如圖7-2。還需要能夠歸檔所有員工和所有部門到一個文件。

 

圖 7-2

         使用的Core Data框架,大多數功能可以自動提供,這主要通過名爲託管對象上下文(managed object context,或只是“上下文”,類NSManagedObjectContext的實例)來實現。託管對象的上下文就像一個網關,通過它可以訪問底層的框架對象集合——這些對象集合統稱爲持久化堆棧(persistence stack)——它在應用程序和外部數據存儲的對象之間提供訪問通道。在堆棧的底部是持久化對象存儲(persistent object stores),如圖7-3所示。

 

圖7-3

         Core Data不侷限於基於文檔的應用程序,也能創建基於Core Data的無用戶界面的Utility應用。當然其他應用程序也能使用Core Data的。

7.1.1.1.              託管對象和上下文(Managed Objects and Contexts)

         可以把託管對象上下文作爲一個智能便箋。當從持久化存儲中獲取對象時,這些對象的臨時副本會在便箋上形成一個對象圖(或者對象圖的集合。對象圖即對象 + 對象之間的聯繫),然後便可以任意修改這些對象。除非保存這些修改,否則持久化存儲是不會受影響的。

         Core Data框架中的模型對象(Model Objects,它是一種含有應用程序數據的對象類型,提供對數據的訪問並實現邏輯來處理數據)稱爲託管對象。所有託管對象都必須通過託管對象上下文進行註冊。該上下文對象允許在圖中加入或刪除對象,以及跟蹤圖中對象的變化,並因此可以提供撤銷(undo)和重做(redo)的支持。當準備好保存對託管對象所做的修改時,託管對象上下文負責確保那些對象處於正確的狀態。

         如果要保存所做的更改,上下文首先要驗證對象是有效的。如果對象有效,所作的更改會被寫到持久化存儲中,創建對象會添加新記錄,刪除對象則會刪除記錄。

         可以在應用程序中使用多個上下文。對於持久化存儲中的每一個對象,只有唯一的一個託管對象和給定的上下文相關聯。從不同的角度考慮,持久化存儲中的一個給定的對象,可在多個上下文中同時被編輯。然而,每個上下文都有它自己的對應着源對象的託管對象,每個託管對象都單獨編輯。這樣保存時就會導致數據不一致。Core Data提供了一些方法來處理這個問題,比如使用- (void)refreshObject:(NSManagedObject *)object mergeChanges: (BOOL)flag方法從持久化存儲中獲取最新的值來更新託管對象的持久化屬性。

7.1.1.2.       獲取數據請求(Fetch Requests)

         使用託管對象上下文來檢索數據時,會創建一個獲取請求(fetch request,類NSFetchRequest的實例)的所有員工,按照工資最高到最低排序”。獲取請求由三部分組成。最簡單的獲取請求必須指定一個實體的名稱(這就暗示每次只能取一種實體類型)。獲取請求也可能包含一個謂詞對象,指定對象必須符合的條件(比如“市場部的所有員工”)。獲取請求還包含一個排序描述方式對象的數組,指定對象應該出現的順序(比如“按照工資最高到最低排序”),如圖7-4所示。

 

圖 7-4

         發送獲取請求給託管對象上下文,它從與持久化存儲相關聯的數據源中返回的匹配請求的對象(也可能沒有返回)。由於所有託管對象必須在託管對象上下文中註冊,從獲取請求返回的對象自動註冊在用來獲取的上下文中。上面說到,對於持久化存儲中的每一個對象,只有唯一的一個託管對象和給定的上下文相關聯。如果上下文中已經包含了一個託管對象作爲獲取請求返回的對象,那麼這個託管對象則作爲獲取請求結果返回。

         Core Data框架試圖儘可能的高效。Core Data是需求驅動的,因此它只會創建實際需要的對象,不會額外創建更多對象。對象圖並不代表持久化存儲中的所有對象。僅指定一個持久化存儲並不會把任何數據對象註冊到託管對象上下文中。當從持久化存儲中獲取對象的一個子集,只能得到請求的對象。如果不再需要一個對象,默認情況下它會被釋放。(這當然與從對象圖中刪除對象不一樣。)

7.1.1.3.       持久化存儲協調器

         如上所述,在應用程序和外部數據存儲的對象之間提供訪問通道的框架對象集合統稱爲持久化堆棧(persistence stack)。在堆棧頂部的是託管對象上下文下,在堆棧底部的是持久化對象存儲(persistent object stores)。在託管對象上下文和持久化對象存儲之間便是持久化存儲協調器(persistent store coordinator)。應用程序通過類NSPersistentStoreCoordinator的實例訪問持久化對象存儲。

         持久化存儲協調器爲一或多個託管對象上下文提供一個訪問接口,使其下層的多個持久化存儲可以表現爲單一一個聚合存儲。一個託管對象上下文可以基於持久化存儲協調器下的所有數據存儲來創建一個對象圖。持久化存儲協調器只能與一個託管對象模型相關聯。如果想把不同的實體放到不同存儲中,必須依據託管對象模型中定義的配置把模型實體分區(partition)。

         圖7-5顯示了一個例子,把員工和部門存儲在一個文件中,並把客戶和公司存儲在另一個文件中。當獲取對象時,他們會自動從相應的文件中返回;當保存更改時,會自動歸檔到相應的文件。

 

圖 7-5

7.1.1.4.       持久化存儲(Persistent Stores)

         一個特定的持久化對象存儲是與單個文件或其他外部數據存儲相關聯的,負責存儲中的數據和託管對象上下文中的對象之間的映射。通常情況下,讀者與持久化對象存儲之間只有一個交互,就是在應用程序中爲一個新的外部數據存儲指定位置(例如,當用戶打開或保存文檔)。Core Data的大多數交互則通過託管對象上下文實現。

         應用程序代碼中——特別是應用程序中託管對象的相關邏輯——不應該爲持久化存儲中的數據存儲作出任何假設(即由Core Data自行處理)。Core Data爲幾種文件格式提供原生支持,選擇使用哪一個取決於應用程序的需求。應用程序體系架構保持不變,也應該能在應用程序的某個階段,選擇一個不同的文件格式。此外,如果應用程序經過適當的抽象,那麼不增加任何操作,就能夠利用框架的後續增強功能。例如——即使在剛開始時,只能從本地文件系統獲取數據——如果一個應用程序沒有假設從哪裏獲取數據,同時後面的某個階段又想爲一種新的遠程持久化存儲類型添加支持,它也應該能夠使用這種新類型而不用修改代碼。

知識點

         儘管Core Data雖然支持的SQLite作爲其持久化存儲類型之一,Core Data也無法管理任意的SQLite數據庫。要使用SQLite數據庫,Core Data必須自行創建和管理數據庫。

7.1.1.5.       持久化文件(Persistent Documents)

         可以通過程序創建和配置持久化堆棧。然而,在許多情況下,只是想創建一個基於文檔的應用程序來讀寫文件。使用類NSDocument的子類NSPersistentDocument,便可輕鬆的利用Core Data完成此事。默認情況下,NSPersistentDocument實例便已經創建了自己的即用型持久化堆棧,包括一個託管對象上下文,和單個持久化對象存儲。在這種情況下,有文檔和外部數據存儲之間便建立起一個“一對一”映射。

NSPersistentDocument類提供了方法來訪問文檔的託管對象上下文,並提供NSDocument方法的標準實現來讀寫文件,這些操作都是在Core Data框架內進行的。默認情況下,不必編寫任何額外的代碼來處理對象的持久化。一個持久化文檔的撤消(undo)功能也被集成在託管對象上下文。

7.1.2.           託管對象(Managed Objects)和託管對象模型(Managed Object Model)

         爲了既管理的對象圖,也支持對象持久化,Core Data需要對它操作的對象進行詳盡的描述。託管對象模型(managed object model)是一個結構(schema),用來描述應用程序中的託管對象,或實體,如圖7-6所示。通常使用Xcode的數據模型設計工具來創建託管對象模型。(也可以在運行時使用代碼來建模。)它是類NSManagedObjectModel的一個實例。

 

圖 7-6

         該模型由一個實體描述對象(類NSEntityDescription實例)的集合組成,每個對象提供一個實體的元數據(metadata),包括實體名,實體在應用程序中的類名(類名不必要與實體名稱一樣),實體的屬性和實體之間關係。實體的屬性和關係依次由屬性和關係描述對象表示出來,如圖7-7所示。

 

圖 7-7

         託管對象必須是NSManagedObject或者NSManagedObject子類的任一實例。NSManagedObject能夠表述任何實體。它使用一個私有的內部存儲,以維護其屬性,並實現託管對象所需的所有基本行爲。託管對象有一個指向實體描述的引用。實體描述表述了實體的元數據,包括實體的名稱,實體的屬性和實體之間的關係。可以創建NSManagedObject子類來實現實體的其他行爲。

7.1.3.           基本實現

       1、指定存儲數據文件

?View Code OBJC
 
1
2
3
4
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths lastObject];
 
NSString *persistentStorePath = [[documentsDirectory stringByAppendingPathComponent:@"TopSongs.sqlite"] retain];

       2、創建託管對象模型

?View Code OBJC
 
1
NSManagedObjectModel  *managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];

       3、創建持久化存儲協調器,並使用SQLite數據庫做持久化存儲

?View Code OBJC
 
1
2
3
4
NSURL *storeUrl = [NSURL fileURLWithPath:self.persistentStorePath];
NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
NSError *error = nil;
NSPersistentStore *persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error];

       4、創建託管對象上下文

?View Code OBJC
 
1
2
 NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator: self.persistentStoreCoordinator];

       5、創建實體描述對象

?View Code OBJC
 
1
 NSEntityDescription *entityDescription = [[NSEntityDescription entityForName:@"Song" inManagedObjectContext: self.managedObjectContext] retain];

       6、創建託管對象

?View Code OBJC
 
1
 NSManagedObject *managedObject = [[NSManagedObject alloc] initWithEntity:self.entityDescription insertIntoManagedObjectContext:self.managedObjectContext];

       7、保存

?View Code OBJC
 
1
2
 NSError *saveError = nil;
[self.managedObjectContext save:&saveError]

       8、創建獲取數據請求

?View Code OBJC
 
1
2
3
4
5
 NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// 數據排序,Sort key爲NSManagedObject託管對象的一個屬性
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];

       9、獲取持久化存儲中的數據,並對數據進行緩存

?View Code OBJC
 
1
2
3
4
5
6
7
8
 NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc]
        initWithFetchRequest:self.fetchRequest
        managedObjectContext:self.managedObjectContext
        sectionNameKeyPath:nil
        cacheName:@""];
 
NSError *error;
BOOL success = [self.fetchedResultsController performFetch:&error];

       10、將數據顯示在UITableView中

?View Code OBJC
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
    id  sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = ;
    NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
    // Configure the cell with data from the managed object.
    return cell;
}
 
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    id  sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo name];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    return [self.fetchedResultsController sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
    return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章