iOS CoreData


  我目前的理解,CoreData相當於一個綜合的數據庫管理庫,它支持sqlite,二進制存儲文件兩種形式的數據存儲。而CoreData提供了存儲管理,包括查詢、插入、

刪除、更新、回滾、會話管理、鎖管理等一系列數據庫操作。另外,開發者還可以在xcode中使用 .xcdatamodel 擴展名的文件,以圖形化的形式編輯數據模型,這里包括了

Entities、Properties、Attributes、Relationships四個概念,這里跟關系型數據庫有很大的相似點。

                  下面來看一下CoreData的框架。

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

                PersistentStore

                            這個是數據真正存儲的地方,CodeData提供了兩種存儲的選擇,分別是sqlite和二進制文件。PersistentStore本身並不是objc類,僅僅是數據存儲。

               DataModel

                              對應的objc類為 NSManagedObjectModel,一個典型的應用如:

 

[cpp]  view plain copy
  1. /** 
  2. Returns the managed object model for the application. 
  3. If the model doesn't already exist, it is created by merging all of the models 
  4. found in the application bundle. 
  5. */  
  6. - (NSManagedObjectModel *)managedObjectModel {  
  7. if (managedObjectModel != nil) {  
  8. return managedObjectModel;  
  9. }  
  10. managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];  
  11. return managedObjectModel;  
  12. }  
這里用了iPhone開發中典型的laze loading,而
[cpp]  view plain copy
  1. managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];  

中的nil表示連接項目中所有的 .xcodemodel 文件為一個datamodel,這是一個非常好的方法,把多個entity放在各自的xcodemodel文件中分開管理,然后用這個函數連接起來生成一個datamodel,這樣就可以對應一個persistentStore。

PersistentStoreCoordinator

     對應的objc類為NSPersistentStoreCoordinator,這個類用來控制對PersistentStore的訪問。PersistentStoreCoordinator提供了一些列的高級調用供其他類來使用,對PersistentStore進行讀和寫。下面看一段典型的代碼:

 

[cpp]  view plain copy
  1. /** 
  2. Returns the persistent store coordinator for the application. 
  3. If the coordinator doesn't already exist, it is created and the application's store 
  4. added to it. 
  5. */  
  6. - (NSPersistentStoreCoordinator *)persistentStoreCoordinator {  
  7.     if (persistentStoreCoordinator != nil) {  
  8.         return persistentStoreCoordinator;  
  9.     }  
  10.     NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]  
  11.     stringByAppendingPathComponent: @"CoreData.sqlite"]];  
  12.     NSError *error;  
  13.     persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]  
  14.     initWithManagedObjectModel: [self managedObjectModel]];  
  15.     if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType  
  16.     configuration:nil URL:storeUrl options:nil error:&error]) {  
  17.     // Handle error  
  18.     }  
  19.     return persistentStoreCoordinator;  
  20. }  
這里默認存儲形式為sqlite,並且存儲文件為CoreData.sqlite,這段代碼比較簡單,創建了persistentStoreCoordinator實例。

 

ManagedObjects

    對應的類為NSManagedObject。上面的CoreData框架圖中有Entities,Entity定義了數據的結構,但他並不是數據,真正的數據實例是NSManagedObject類或他的子類。

    NSManagedObject類支持Key-Value 編碼(KVC),像NSDictionary差不多。NSManagedObject提供了valueForKey:和setValue:forKey:用來設置和查詢的方法。另外他也提供了對關系操作的方法。

   下面是幾個典型的代碼案例:

 

[cpp]  view plain copy
  1. NSDate *timeStamp = [managedObject valueForKey:@"timeStamp"];  

[cpp]  view plain copy
  1. [managedObject setValue:[NSDate date] forKey:@"timeStamp"];  

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

 

 

[cpp]  view plain copy
  1. NSString *employerName = [managedObject valueForKeyPath:@"whereIWork.name"];  

 

ManagedObjectsContext

    對應的類為NSManagedObjectsContext。 這個類是一個用戶對persistentStore操作的網關,他維護了用戶創建或者加載的managed objects。他記錄了用戶對managed objects的所有改變,以便用來undo或redo,另外當用戶要存儲現在的managed objects到persistentstore時,只需調用managedObjectsContext的save方法就行了。

    每個應用至少需要一個context,當然可以同時存在多個context,比如多線程時,如NSOperationQueue。context並不是線程安全的,因此在這種情況中用戶要自己做好安全工作。

    下面是一個簡單應用實例。

[cpp]  view plain copy
  1. /** 
  2. Returns the managed object context for the application. 
  3. If the context doesn't already exist, it is created and bound to the persistent 
  4. store coordinator for the application. 
  5. */  
  6. - (NSManagedObjectContext *) managedObjectContext {  
  7.     if (managedObjectContext != nil) {  
  8.         return managedObjectContext;  
  9.     }  
  10.     NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];  
  11.     if (coordinator != nil) {  
  12.         managedObjectContext = [[NSManagedObjectContext alloc] init];  
  13.         [managedObjectContext setPersistentStoreCoordinator: coordinator];  
  14.     }  
  15.     return managedObjectContext;  
  16. }  

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

 

FetchRequest(FetchRequestController)

 

這里重點講FetchRequestController,其實用戶打交道最多的就是這個控制器了。要講的東西很多,放到下面一篇吧。

這篇文章重點講講CoreData的Fetched Results Controller。

             對應的objc類為NSFetchedResultsController。這個類是用來管理CoreData Fetch request返回的對象的。

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

[cpp]  view plain copy
  1. - (NSFetchedResultsController *)fetchedResultsController {    
  2.     if (fetchedResultsController != nil) {    
  3.         return fetchedResultsController;    
  4.     }    
  5.     /*  
  6.     Set up the fetched results controller.  
  7.     */    
  8.     // Create the fetch request for the entity.    
  9.     NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];    
  10.     // Edit the entity name as appropriate.    
  11.     NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event"    
  12.     inManagedObjectContext:managedObjectContext];    
  13.     [fetchRequest setEntity:entity];    
  14.     // Set the batch size to a suitable number.    
  15.     [fetchRequest setFetchBatchSize:20];    
  16.     // Edit the sort key as appropriate.    
  17.     NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]    
  18.     initWithKey:@"timeStamp" ascending:NO];    
  19.     NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor,nil];    
  20.     [fetchRequest setSortDescriptors:sortDescriptors];    
  21.     // Edit the section name key path and cache name if appropriate.    
  22.     // nil for section name key path means "no sections".    
  23.     NSFetchedResultsController *aFetchedResultsController =    
  24.     [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest    
  25.     managedObjectContext:managedObjectContext sectionNameKeyPath:nil    
  26.     cacheName:@"Root"];    
  27.     aFetchedResultsController.delegate = self;    
  28.     self.fetchedResultsController = aFetchedResultsController;    
  29.     [aFetchedResultsController release];    
  30.     [fetchRequest release];    
  31.     [sortDescriptor release];    
  32.     [sortDescriptors release];    
  33.     return fetchedResultsController;    
  34. }    

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

[cpp]  view plain copy
  1. NSError *error = nil;    
  2. if(![[self  fetchedResultsController]performFetch: &error]){    
  3.     //handle the error appropriately    
  4.     NSLog(@"Unresolved error %@, %@", error, [error userInfo]);    
  5.     exit(-1);    
  6. }    

          這段代碼一般會放在viewDidLoad函數中,初始化之后,fetchedResultsController就與數據相連接了,之后要取數據都能直接從這個控制器提供的方法中去取。

            實現這個控制器,最關鍵的還要實現Fetched Results Controller Delegate Methods。控制器與數據源連接后,控制器監視器會時刻監視着數據源,當數據源發生

改變后,監視器會調用對應的協議方法,改協議總共要實現四個方法,分別為:

[cpp]  view plain copy
  1. - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller;    
  2. - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller;    
  3. - (void)controller:(NSFetchedResultsController *)controller    
  4.    didChangeObject:(id)anObject    
  5.        atIndexPath:(NSIndexPath *)indexPath    
  6.      forChangeType:(NSFetchedResultsChangeType)type    
  7.       newIndexPath:(NSIndexPath *)newIndexPath;    
  8. - (void)controller:(NSFetchedResultsController *)controller    
  9.   didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo    
  10.            atIndex:(NSUInteger)sectionIndex    
  11.      forChangeType:(NSFetchedResultsChangeType)type;    

 

              下面依次來解釋這四個協議方法。

              1.  - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller

            當控制器監控的數據發生改變時,如對象被刪除,有插入,更新等,監視器會在數據發生改變前意識到這個情況,此時就會調用這個函數。往往我們用列表的形式

表現數據,此時意味着屏幕上的數據即將過時,因為數據馬上要改變了,這是這個協議方法的工作就是通知列表數據馬上要更新的消息,往往代碼是這樣實現的。 
[cpp]  view plain copy
  1. - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {    
  2.     [self.tableView beginUpdates];    
  3. }   

 

           2. - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller

            當fetchedResultsController完成對數據的改變時,監視器會調用這個協議方法。在上面提到的情況,這個方法要通知列表數據已經完成,可以更新顯示的數據這個

消息,因此通常的實現是這樣的。

 

[cpp]  view plain copy
  1. - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {    
  2.     [self.tableView endUpdates];    
  3. }    
  3. - (void)controller:(NSFetchedResultsController *)controller

 

 

              didChangeObject:(id)anObject 

                        atIndexPath:(NSIndexPath *)indexPath 

                  forChangeType:(NSFetchedResultsChangeType)type 

                    newIndexPath:(NSIndexPath *)newIndexPath

               當fetchedResultsController發現指定的對象有改變時,監視器會調用這個協議方法。這里改變的類型從列表中體現有 更新、插入、刪除或者行的移動。因此這個

方法要實現所有的這些方法,以應對任何一種改變。下面是這個方法的標准實現。

[cpp]  view plain copy
  1. - (void)controller:(NSFetchedResultsController *)controller    
  2.    didChangeObject:(id)anObject    
  3.        atIndexPath:(NSIndexPath *)indexPath    
  4.      forChangeType:(NSFetchedResultsChangeType)type    
  5.       newIndexPath:(NSIndexPath *)newIndexPath {    
  6.     switch(type) {    
  7.         case NSFetchedResultsChangeInsert:    
  8.             [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]     
  9.                             withRowAnimation:UITableViewRowAnimationFade];    
  10.             break;    
  11.         case NSFetchedResultsChangeDelete:    
  12.             [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]    
  13.                             withRowAnimation:UITableViewRowAnimationFade];    
  14.             break;    
  15.         case NSFetchedResultsChangeUpdate: {    
  16.             NSString *sectionKeyPath = [controller sectionNameKeyPath];    
  17.             if (sectionKeyPath == nil)    
  18.                 break;    
  19.             NSManagedObject *changedObject = [controller objectAtIndexPath:indexPath];    
  20.             NSArray *keyParts = [sectionKeyPath componentsSeparatedByString:@"."];    
  21.             id currentKeyValue = [changedObject valueForKeyPath:sectionKeyPath];    
  22.             for (int i = 0; i < [keyParts count] - 1; i++) {    
  23.                 NSString *onePart = [keyParts objectAtIndex:i];    
  24.                 changedObject = [changedObject valueForKey:onePart];    
  25.             }    
  26.             sectionKeyPath = [keyParts lastObject];    
  27.             NSDictionary *committedValues = [changedObject committedValuesForKeys:nil];    
  28.             if ([[committedValues valueForKeyPath:sectionKeyPath]isEqual:currentKeyValue])    
  29.                 break;    
  30.             NSUInteger tableSectionCount = [self.tableView numberOfSections];    
  31.             NSUInteger frcSectionCount = [[controller sections] count];    
  32.             if (tableSectionCount != frcSectionCount) {    
  33.                 // Need to insert a section    
  34.                 NSArray *sections = controller.sections;    
  35.                 NSInteger newSectionLocation = -1;    
  36.                 for (id oneSection in sections) {    
  37.                     NSString *sectionName = [oneSection name];    
  38.                     if ([currentKeyValue isEqual:sectionName]) {    
  39.                         newSectionLocation = [sections indexOfObject:oneSection];    
  40.                         break;    
  41.                     }    
  42.                 }    
  43.                 if (newSectionLocation == -1)    
  44.                     return// uh oh    
  45.                 if (!((newSectionLocation == 0) && (tableSectionCount == 1)    
  46.                        && ([self.tableView numberOfRowsInSection:0] == 0)))    
  47.                     [self.tableView insertSections:[NSIndexSet indexSetWithIndex:newSectionLocation]    
  48.                                   withRowAnimation:UITableViewRowAnimationFade];    
  49.                 NSUInteger indices[2] = {newSectionLocation, 0};    
  50.                 newIndexPath = [[[NSIndexPath alloc] initWithIndexes:indiceslength:2] autorelease];    
  51.             }    
  52.         }    
  53.         case NSFetchedResultsChangeMove    
  54.             if (newIndexPath != nil) {    
  55.                 [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]    
  56.                                       withRowAnimation:UITableViewRowAnimationFade];    
  57.                 [self.tableView insertRowsAtIndexPaths: [NSArray arrayWithObject:newIndexPath]    
  58.                                       withRowAnimation: UITableViewRowAnimationRight];    
  59.             }    
  60.             else {    
  61.                 [self.tableView reloadSections:[NSIndexSet    
  62.                 indexSetWithIndex:[indexPath section]]withRowAnimation:UITableViewRowAnimationFade];    
  63.             }    
  64.             break;    
  65.         default:    
  66.             break;    
  67.     }    
  68. }    

 

 

          從上面的代碼可以看出,插入,刪除,移動是比較簡單的,最復雜的是更新。這個代碼是xcode的模板代碼,基本能適用我們遇到的情況,對更新里面的代碼我還不是非常確定,所以這里留着等過幾天完全吃透了再補上。

 

 

           4. - (void)controller:(NSFetchedResultsController *)controller

            didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo

                                atIndex:(NSUInteger)sectionIndex 

                 forChangeType:(NSFetchedResultsChangeType)type

              當改變控制器管理的對象后引起了列表section的變化,此時監視器就會調用這個協議函數。

            下面是標准實現。 

[cpp]  view plain copy
  1. - (void)controller:(NSFetchedResultsController *)controller    
  2.   didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo    
  3.            atIndex:(NSUInteger)sectionIndex    
  4.      forChangeType:(NSFetchedResultsChangeType)type {    
  5.     switch(type) {    
  6.         case NSFetchedResultsChangeInsert:    
  7.             if (!((sectionIndex == 0) && ([self.tableView numberOfSections] == 1)    
  8.                              && ([self.tableView numberOfRowsInSection:0] == 0)))    
  9.                 [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]    
  10.                               withRowAnimation:UITableViewRowAnimationFade];    
  11.             break;    
  12.         case NSFetchedResultsChangeDelete:    
  13.             if (!((sectionIndex == 0) && ([self.tableView numberOfSections] == 1)    
  14.                              && ([self.tableView numberOfRowsInSection:0] == 0)))    
  15.                 [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]    
  16.                               withRowAnimation:UITableViewRowAnimationFade];    
  17.             break;    
  18.         case NSFetchedResultsChangeMove:    
  19.         case NSFetchedResultsChangeUpdate:    
  20.         default:    
  21.             break;    
  22.     }    
  23. }    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM