CoreData初級理解

基本結構

CoreData相當于一個綜合的數據存儲和管理中心,它支持sqlite,二進制存儲文件兩種形式的數據存儲。而CoreData提供了存儲管理,包括查詢、插入、刪除、更新、回滾、會話管理、鎖管理等一系列數據庫操作。另外,開發(fā)者還可以在Xcode中使用 .xcdatamodel 擴展名的文件,以圖形化的形式編輯數據模型,這里包括Entities、Properties、Attributes、Relationships四個概念,這里跟關系型數據庫有很大的相似點。
下面來看一下CoreData的框架圖。

CoreData框架圖

常用類型

一次了解一下 **PersistentStore、DataModel、PersistentStoreCoordinator、ManagedObjects、ManagedObjectsContext、FetchRequest **這些概念。

  • 1.PersistentStore
    這個是數據真正存儲的地方,CodeData提供了兩種存儲的選擇,分別是sqlite和二進制文件。PersistentStore本身并不是objc類,僅僅是數據存儲
  • 2.DataModel
    對應的objc類為 NSManagedObjectModel,一個典型的應用如:
 //Returns the managed object model for the application. 
 If the model doesn't already exist, it is created by merging all of the models 
 found in the application bundle. 
 
 - (NSManagedObjectModel *)managedObjectModel {  
    if (managedObjectModel != nil) {  
    return managedObjectModel;  
    }  
 managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];  
 return managedObjectModel;  
}  

// 這里用了iPhone開發(fā)中典型的laze loading,
managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain]; 
// 參數中的nil表示連接項目中所有的 .xcodemodel 文件為一個datamodel,這是一個非常好的方法,
// 把多個entity放在各自的xcodemodel文件中分開管理,然后用這個函數連接起來生成一個datamodel,這樣就可以對應一個persistentStore。
  • 3.PersistentStoreCoordinator
    對應的objc類為NSPersistentStoreCoordinator,這個類用來控制對PersistentStore的訪問。PersistentStoreCoordinator提供了一些列的高級調用供其他類來使用,對PersistentStore進行讀和寫,比如增查刪改。下面看一段典型的代碼:
//Returns the persistent store coordinator for the application. 
If the coordinator doesn't already exist, it is created and the application's store 
added to it. 
 
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {  
   if (persistentStoreCoordinator != nil) {  
       return persistentStoreCoordinator;  
   }  
   NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]  
   stringByAppendingPathComponent: @"CoreData.sqlite"]];  
   NSError *error;  
   persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]  
   initWithManagedObjectModel: [self managedObjectModel]];  
   if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType  
   configuration:nil URL:storeUrl options:nil error:&error]) {  
   // Handle error  
   }  
   return persistentStoreCoordinator;  
}  

這里默認存儲形式為sqlite,并且存儲文件為CoreData.sqlite,這段代碼比較簡單,創(chuàng)建了persistentStoreCoordinator實例。

  • 4.ManagedObjects
    對應的objc類為NSManagedObject。上面的CoreData框架圖中有Entities,Entity定義了數據的結構,但他并不是數據,真正的數據實例是NSManagedObject類或他的子類
    NSManagedObject類支持Key-Value 編碼(KVC),像NSDictionary差不多。NSManagedObject提供了valueForKey:和setValue:forKey:用來設置和查詢的方法。另外他也提供了對關系操作的方法。
    下面是幾個典型的代碼案例:
NSDate *timeStamp = [managedObject valueForKey:@"timeStamp"];  
[managedObject setValue:[NSDate date] forKey:@"timeStamp"];  

另外KVC也支持keypath,如有兩個數據entity,一個是Employee,一個事Employer,Employee中有個屬性石whereIWork,而這個屬性用relationship連接到了對應的Employer,Employer中有個屬性石name,這樣要查詢一個Employer的name,可以用keypath的形式,whereIWork.name。

NSString *employerName = [managedObject valueForKeyPath:@"whereIWork.name"];

  • 5.ManagedObjectsContext
    對應的objc類為NSManagedObjectsContext。 這個類是一個用戶對persistentStore操作的網關,他維護了用戶創(chuàng)建或者加載的managed objects。他記錄了用戶對managed objects的所有改變,以便用來undo或redo,另外當用戶要存儲現在的managed objects到persistentstore時,只需調用managedObjectsContext的save方法就行了。
    每個應用至少需要一個context,當然可以同時存在多個context,比如多線程時,如NSOperationQueue。context并不是線程安全的,因此在這種情況中用戶要自己做好安全工作。
    下面是一個簡單應用實例。
/** 
Returns the managed object context for the application. 
If the context doesn't already exist, it is created and bound to the persistent 
store coordinator for the application. 
*/  
- (NSManagedObjectContext *) managedObjectContext {  
    if (managedObjectContext != nil) {  
        return managedObjectContext;  
    }  
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];  
    if (coordinator != nil) {  
        managedObjectContext = [[NSManagedObjectContext alloc] init];  
        [managedObjectContext setPersistentStoreCoordinator: coordinator];  
    }  
    return managedObjectContext;  
}  

這個代碼也比較簡單,不做解釋了。

FetchRequest(FetchRequestController)

這里重點講FetchRequestController,其實用戶打交道最多的就是這個控制器了。 對應的objc類為NSFetchedResultsController。這個類是用來管理CoreData Fetch request返回的對象的。

在創(chuàng)建這個控制器之前,必須先創(chuàng)建fetch request。 fetch request描述了詳細的查詢規(guī)則,還可以添加查詢結果的排序描述(sort descriptor)。fetchResultsController根據已經創(chuàng)建完的fetch request來創(chuàng)建, 它是NSFetchedResultsController的實例,這個實例的主要任務就是使用fetch request來保證它所關聯的數據的新鮮性。創(chuàng)建了fetchResultsController實例后要做一下初始化,一般初始化是向這個控制器發(fā)送PerformFetch消息,下面是這一過程的代碼。


- (NSFetchedResultsController *)fetchedResultsController {    
    if (fetchedResultsController != nil) {    
        return fetchedResultsController;    
    }    
    /*  
    Set up the fetched results controller.  
    */    
    // Create the fetch request for the entity.    
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];    
    // Edit the entity name as appropriate.    
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event"    
    inManagedObjectContext:managedObjectContext];    
    [fetchRequest setEntity:entity];    
    // Set the batch size to a suitable number.    
    [fetchRequest setFetchBatchSize:20];    
    // Edit the sort key as appropriate.    
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]    
    initWithKey:@"timeStamp" ascending:NO];    
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor,nil];    
    [fetchRequest setSortDescriptors:sortDescriptors];    
    // Edit the section name key path and cache name if appropriate.    
    // nil for section name key path means "no sections".    
    NSFetchedResultsController *aFetchedResultsController =    
    [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest    
    managedObjectContext:managedObjectContext sectionNameKeyPath:nil    
    cacheName:@"Root"];    
    aFetchedResultsController.delegate = self;    
    self.fetchedResultsController = aFetchedResultsController;    
    return fetchedResultsController;    
}    

這個函數用來創(chuàng)建FetchedResultsController,過程還是比較簡單的,下面是初始化這個控制器代碼。


NSError *error = nil;    
if(![[self  fetchedResultsController]performFetch: &error]){    
    //handle the error appropriately    
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);    
    exit(-1);    
}    

這段代碼一般會放在viewDidLoad函數中,初始化之后,fetchedResultsController就與數據相連接了,之后要取數據都能直接從這個控制器提供的方法中去取。
實現這個控制器,最關鍵的還要實現Fetched Results Controller Delegate Methods??刂破髋c數據源連接后,控制器監(jiān)視器會時刻監(jiān)視著數據源,當數據源發(fā)生
改變后,監(jiān)視器會調用對應的協(xié)議方法,改協(xié)議總共要實現四個方法,分別為:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller;    
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller;    
- (void)controller:(NSFetchedResultsController *)controller    
   didChangeObject:(id)anObject    
   atIndexPath:(NSIndexPath *)indexPath    
   forChangeType:(NSFetchedResultsChangeType)type    
   newIndexPath:(NSIndexPath *)newIndexPath;    
- (void)controller:(NSFetchedResultsController *)controller    
   didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo    
   atIndex:(NSUInteger)sectionIndex    
    forChangeType:(NSFetchedResultsChangeType)type;    

下面依次來解釋這四個協(xié)議方法。
*1. - (void)controllerWillChangeContent:(NSFetchedResultsController )controller

當控制器監(jiān)控的數據發(fā)生改變時,如對象被刪除,有插入,更新等,監(jiān)視器會在數據發(fā)生改變前意識到這個情況,此時就會調用這個函數。往往我們用列表的形式表現數據,此時意味著屏幕上的數據即將過時,因為數據馬上要改變了,這是這個協(xié)議方法的工作就是通知列表數據馬上要更新的消息,往往代碼是這樣實現的。

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {    
    [self.tableView beginUpdates];    
}   

*2. - (void)controllerDidChangeContent:(NSFetchedResultsController )controller
當fetchedResultsController完成對數據的改變時,監(jiān)視器會調用這個協(xié)議方法。在上面提到的情況,這個方法要通知列表數據已經完成,可以更新顯示的數據這個消息,因此通常的實現是這樣的。

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {    
    [self.tableView endUpdates];    
}    

** 3. - (void)controller:(NSFetchedResultsController )controller*
** didChangeObject:(id)anObject **
** atIndexPath:(NSIndexPath )indexPath **
** forChangeType:(NSFetchedResultsChangeType)type **
** newIndexPath:(NSIndexPath )newIndexPath

當fetchedResultsController發(fā)現指定的對象有改變時,監(jiān)視器會調用這個協(xié)議方法。這里改變的類型從列表中體現有 更新、插入、刪除或者行的移動。因此這個方法要實現所有的這些方法,以應對任何一種改變。下面是這個方法的標準實現。

- (void)controller:(NSFetchedResultsController *)controller    
   didChangeObject:(id)anObject    
       atIndexPath:(NSIndexPath *)indexPath    
     forChangeType:(NSFetchedResultsChangeType)type    
      newIndexPath:(NSIndexPath *)newIndexPath {    
    switch(type) {    
        case NSFetchedResultsChangeInsert:    
            [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]     
                            withRowAnimation:UITableViewRowAnimationFade];    
            break;    
        case NSFetchedResultsChangeDelete:    
            [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]    
                            withRowAnimation:UITableViewRowAnimationFade];    
            break;    
        case NSFetchedResultsChangeUpdate: {    
            NSString *sectionKeyPath = [controller sectionNameKeyPath];    
            if (sectionKeyPath == nil)    
                break;    
            NSManagedObject *changedObject = [controller objectAtIndexPath:indexPath];    
            NSArray *keyParts = [sectionKeyPath componentsSeparatedByString:@"."];    
            id currentKeyValue = [changedObject valueForKeyPath:sectionKeyPath];    
            for (int i = 0; i < [keyParts count] - 1; i++) {    
                NSString *onePart = [keyParts objectAtIndex:i];    
                changedObject = [changedObject valueForKey:onePart];    
            }    
            sectionKeyPath = [keyParts lastObject];    
            NSDictionary *committedValues = [changedObject committedValuesForKeys:nil];    
            if ([[committedValues valueForKeyPath:sectionKeyPath]isEqual:currentKeyValue])    
                break;    
            NSUInteger tableSectionCount = [self.tableView numberOfSections];    
            NSUInteger frcSectionCount = [[controller sections] count];    
            if (tableSectionCount != frcSectionCount) {    
                // Need to insert a section    
                NSArray *sections = controller.sections;    
                NSInteger newSectionLocation = -1;    
                for (id oneSection in sections) {    
                    NSString *sectionName = [oneSection name];    
                    if ([currentKeyValue isEqual:sectionName]) {    
                        newSectionLocation = [sections indexOfObject:oneSection];    
                        break;    
                    }    
                }    
                if (newSectionLocation == -1)    
                    return; // uh oh    
                if (!((newSectionLocation == 0) && (tableSectionCount == 1)    
                       && ([self.tableView numberOfRowsInSection:0] == 0)))    
                    [self.tableView insertSections:[NSIndexSet indexSetWithIndex:newSectionLocation]    
                                  withRowAnimation:UITableViewRowAnimationFade];    
                NSUInteger indices[2] = {newSectionLocation, 0};    
                newIndexPath = [[[NSIndexPath alloc] initWithIndexes:indiceslength:2] autorelease];    
            }    
        }    
        case NSFetchedResultsChangeMove    
            if (newIndexPath != nil) {    
                [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]    
                                      withRowAnimation:UITableViewRowAnimationFade];    
                [self.tableView insertRowsAtIndexPaths: [NSArray arrayWithObject:newIndexPath]    
                                      withRowAnimation: UITableViewRowAnimationRight];    
            }    
            else {    
                [self.tableView reloadSections:[NSIndexSet    
                indexSetWithIndex:[indexPath section]]withRowAnimation:UITableViewRowAnimationFade];    
            }    
            break;    
        default:    
            break;    
    }    
}    

從上面的代碼可以看出,插入,刪除,移動是比較簡單的,最復雜的是更新。這個代碼是xcode的模板代碼,基本能適用我們遇到的情況.

** 4. - (void)controller:(NSFetchedResultsController )controller*
** didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo**
** atIndex:(NSUInteger)sectionIndex **
** forChangeType:(NSFetchedResultsChangeType)type**
當改變控制器管理的對象后引起了列表section的變化,此時監(jiān)視器就會調用這個協(xié)議函數。
下面是標準實現。

- (void)controller:(NSFetchedResultsController *)controller    
  didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo    
           atIndex:(NSUInteger)sectionIndex    
     forChangeType:(NSFetchedResultsChangeType)type {    
    switch(type) {    
        case NSFetchedResultsChangeInsert:    
            if (!((sectionIndex == 0) && ([self.tableView numberOfSections] == 1)    
                             && ([self.tableView numberOfRowsInSection:0] == 0)))    
                [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]    
                              withRowAnimation:UITableViewRowAnimationFade];    
            break;    
        case NSFetchedResultsChangeDelete:    
            if (!((sectionIndex == 0) && ([self.tableView numberOfSections] == 1)    
                             && ([self.tableView numberOfRowsInSection:0] == 0)))    
                [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]    
                              withRowAnimation:UITableViewRowAnimationFade];    
            break;    
        case NSFetchedResultsChangeMove:    
        case NSFetchedResultsChangeUpdate:    
        default:    
            break;    
    }    
}   

參考鏈接

CoreData之FetchRequestController
CoreData入門
深入淺出 Cocoa 之 Core Data
XMPP介紹二:Core Data

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結起來就是把...
    Dove_iOS閱讀 27,624評論 30 472
  • 我目前的理解,CoreData相當于一個綜合的數據存儲和管理中心,它支持sqlite,二進制存儲文件兩種形式的數據...
    航彪彪閱讀 593評論 0 0
  • 版權聲明:本文為博主原創(chuàng)文章,未經博主允許不得轉載。 我目前的理解,CoreData相當于一個綜合的數據存儲和管理...
    LeaderBiao閱讀 508評論 0 2
  • 趙茂宇 少年以文學為矢 射殺宇天童話 皮囊中與黑暗斗高下 自以遨游世界 劃破長天 深邃巨人矣 亙于宇宙間 頭頂...
    雪兒少年閱讀 257評論 0 2
  • 2017年6月2日打卡 周五,天氣悶熱。 提綱記事: 1,子冉值班。每當這時候,全家老少如臨大敵總動員,猴奶奶也從...
    沈曼柔閱讀 338評論 2 2

友情鏈接更多精彩內容