conanwin

导航

coredata 基础

 

http://blog.csdn.net/feosun/article/details/8226534

基本概念
在CoreData有一些概念刚学习的时候不是很容易理解,还是要单独拿出来来梳理一下,这样学后面的内容不会感觉吃力。


♥ 表结构:NSEntityDescription
♥ 表记录:NSManagedObject

--------------------------------------------------------------------------------

♥ 数据库存放方式:NSPersistentStoreCoordinator(持久化存储协调者)
♥ 数据库操作:NSManagedObjectContext(被管理的对象上下文)
还有一些类NSManagedObjectModel、NSFetchRequest什么的,具体项目就会有体会,下面来实战一下。

新建一个项目,项目模板基于“Master-Detail Application”,点击“Next”按钮,项目命名为“SimpleCoreData”,并勾选“Use Core Data”,点击“Next”,选择项目保存的目录,点击“Create”按钮,项目创建完毕。


代码分析
比以前创建的简单项目多了不少代码,还有xcdatamodeld文件,慢慢分析代码,AppDelegate.h头文件中,添加了三个property

@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
再分析AppDelegate.m文件,有对应的三个方法来返回各自对应的对象

#pragma mark - Core Data stack
 
- (NSManagedObjectContext *)managedObjectContext
{
...
    return __managedObjectContext;
}
 
- (NSManagedObjectModel *)managedObjectModel
{
... 
    return __managedObjectModel;
}
 
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
...
    return __persistentStoreCoordinator;
}
这些对象在哪里被调用的呢,打开MasterViewController.m,在初始化函数中,我们看到通过获取delegate,再通过delegate调用方法managedObjectContext,这样就得到了这个NSManagedObjectContext对象,NSManagedObjectContext对象它会跟NSPersistentStoreCoordinator对象打交道,NSPersistentStoreCoordinator会去处理底层的存储方式。

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        self.title = NSLocalizedString(@"Master", @"Master");
        id delegate = [[UIApplication sharedApplication] delegate];
        self.managedObjectContext = [delegate managedObjectContext];
    }
    return self;
}查询实体
分析MasterViewController.m的代码发现以下函数的调用顺序。

♥ -tableView:(UITableView *)tableView cellForRowAtIndexPath:
♥ -configureCell:atIndexPath:
♥ -fetchedResultsController
最后是从fetchedResultsController获取到查询结果,那就有必要来分析一下

- (NSFetchedResultsController *)fetchedResultsController
{
    // 如果查询结果已经存在就直接返回__fetchedResultsController
    if (__fetchedResultsController != nil)
    {
        return __fetchedResultsController;
    }
 
    // 1.创建NSFetchRequest对象(相当于SQL语句)
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
 
    // 2.创建查询实体(相当于设置查询哪个表)
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];
 
    // 设置获取数据的批数.
    [fetchRequest setFetchBatchSize:20];
 
    // 3.创建排序描述符,(ascending:是否升序)
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
 
    [fetchRequest setSortDescriptors:sortDescriptors];
 
    // 根据fetchRequest和managedObjectContext来创建aFetchedResultsController对象,并设置缓存名字为"Master".
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"];
 
    // 设置aFetchedResultsController的委托对象为当前类
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;
 
 NSError *error = nil;
    // 获取第一批数据
 if (![self.fetchedResultsController performFetch:&error])
        {
        // 错误处理
     NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
     abort();
 }
 
    return __fetchedResultsController;
}
因为我们设置了aFetchedResultsController的委托NSFetchedResultsControllerDelegate,就要实现它的方法,幸运的是这些方法看起来都像下面这样,直接复制粘贴就行了。

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView beginUpdates];
}
 
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
    switch(type)
    {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
 
        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}
 
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath
{
    UITableView *tableView = self.tableView;
 
    switch(type)
    {
 
        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
 
        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
 
        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;
 
        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}
 
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView endUpdates];
}添加实体- (void)insertNewObject
{
    // 从NSFetchedResultsController中获取NSManagedObjectContext对象
    NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
 
    // 从NSFetchedResultsController中获取实体描述
    NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
 
    // 在被管理上下文中插入一个新的NSManagedObject
    NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
 
    // 字段赋值
    [newManagedObject setValue:[NSDate date] forKey:@"timeStamp"];
 
    // 保存被管理对象上下文,这样刚才的newManagedObject就被保存了
    NSError *error = nil;
    if (![context save:&error])
    {
        // 错误处理.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
}删除实体// 从NSFetchedResultsController中获取NSManagedObjectContext对象
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
 
// 从被管理对象上下文中删除MO对象
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
 
// 保存被管理对象上下文
NSError *error = nil;
if (![context save:&error])
{
    // 错误处理
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}
这个删除对象为什么没有处理TableView的代码,因为我们实现NSFetchedResultsControllerDelegate委托,当对象上下文发生变化时NSFetchedResultsControllerDelegate的实现方法会处理这些事情。

 

这篇文章重点讲讲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 plaincopy
 
  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 plaincopy
 
  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 plaincopy
 
  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 plaincopy
 
  1. - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {    
  2.     [self.tableView beginUpdates];    
  3. }   

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

            当fetchedResultsController完成对数据的改变时,监视器会调用这个协议方法。在上面提到的情况,这个方法要通知列表数据已经完成,可以更新显示的数据这个

消息,因此通常的实现是这样的。

[cpp] view plaincopy
 
  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 plaincopy
 
  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 plaincopy
 
    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. }    

posted on 2015-06-27 23:33  conanwin  阅读(158)  评论(0编辑  收藏  举报