• 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. }    
  • 相关阅读:
    Navicat连接MySQL数据库的一些问题与解决方案
    从select机制谈到epoll机制
    关于VS2017提示I/O文件操作函数需要加上_s的解决办法
    LeetCode初级算法(树篇)
    LeetCode初级算法(动态规划+设计问题篇)
    LeetCode初级算法(其他篇)
    Leetcode初级算法(排序和搜索+数学篇)
    Leetcode初级算法(链表篇)
    Leetcode初级算法(字符串篇)
    机器学习之k-近邻算法
  • 原文地址:https://www.cnblogs.com/conanwin/p/4604893.html
Copyright © 2020-2023  润新知