• ios开发:Core Data概述


    Core Data 概述

    2005年的四月份,Apple 发布了 OS X 10.4,在这个版本中 Core Data 框架发布了。Core Data本身既不是数据库也不是数据库访问框架。相反,Core Data是一个完整的数据模型解决方案。可以简单理解为对持久层的封装,使得我们可以通过可视化建立数据模型,简化数据存取。即使不懂SQL语句,也依然可以使用Core Data。因为Core Data将底层的数据库SQL语句封装成了一套API,并可通过可视化操作来建立数据库的模型和表之间的关系,它甚至在数据变化时会帮你自动处理关系,Core Data还能对非法数据进行过滤。更重要的是,Core Data的NSFetchRequest类可以替代SQL中的Select语句,并提供了NSFetchedResultsController以更高效的方法将查询结果显示在UITableView中。

    Core Data 组件

    Core Data的组件主要由三部分组成:

    Managed Object Model(数据模型): 可以看作是数据库的模型结构。包含了各个实体的定义信息。

    Persistent Store Coordinator (持久化存储协调器):将对象图管理部分和持久化部分捆绑在一起,当它们两者中的任何一部分需要和另一部分交流时,这便需要持久化存储协调器来调节。

    Managed Object Context (管理数据上下文):被管理数据的上下文,实际上是对你所有数据库操作的一个缓存层,会把你所有的操作都先缓存起来避免大量磁盘 IO 造成不流畅,你在操作完数据库后调用其save方法持久化改变。

    上图是Core Data 基本工作原理 可以帮助我们更加深入的了解Core data各个组件间是怎么工作的  下面通过一个例子详细介绍下core data究竟干了什么

    一个Core Data 工程

    在这里我们不需要自己动手去做一个工程  因为xcode已经 为我们提供了一个完整的Core Data 项目 打开我们的xcode  在 iOS -> Application 下选择 Master-Detail Application 命完名字后记得勾选Core Data选项 项目建立后我们可以发现Frameworks中已经有了CoreData.framework一项,并且还多了一个以 .xcdatamodeld结尾的文件,这个 文件定义了我们需要 的数据模型结构。点开后  可以看到左侧有三个选项:Entities,Fetch Request、Configurations。

    Entities

     Entities Core Data 的一个实体 它可以像类一样被继承   为了便于理解我们也可以把它理解为数据库中的一个表,实体里的属性可以看做数据库表中的属性 我们的项目中现在有一个Event实体,这个实体有一个叫timeStamp的属性  这里可以理解为,我们的数据库中有一个叫Event的表  表里有一个叫timeStamp的属性

    Attributes 就是我们上面说的属性,我们可以设置其数据类型,默认值,最大,小值等。需要注意的是这里的空值是NULL,不等同于OC中的nil,更不等同于0和空字符串@“”。

    Relationships 描述多个Entities间的关系:多对一,一对一,继承关系等。当我们指定了一个关系后,我们也最好指定一个反转关系。比如A和B是多对多的关系,那么A指向B的关系Type为To Many,同时设定B指向A的关系Type为To Many,如果A为B的父类那么同时要指定B为A的子类。

    Fetched Property表示了一种弱的、单向的关系。Core Data不支持在persistent store之间建立Relationships,所以Fetched Property可用于建立“松耦合”关系,相似暂时的分组。

    Fetch Request

    在Core Data中我们使用NSFetchRequest类来进行数据请求,从持久存储(persistent store)中获取对象。请求中包含变量(如查找条件)类似我们sql语句中的查找条件,这个我们以后有机会在介绍。

    Configurations

    配置包含了一个名称和若干个相关的实体。一个实体可以出现在多个配置中。我们可以在代码中使用setEntities: forConfiguration:的方法来指定配置。我们也可以使用entitiesForConfiguration:来获取配置。一般说来,如果你想把不同的实体存放在不同的存储中去,就可能用到配置。一个持久化存储协调器(persistent store coordinator)只能有一个被管理的对象模型(managed object model)。所以,默认情况下,和协调器关联的某个存储必须包含同样的实体。要想绕过这个限制,你可以创建一个包含所有实体并集的模型,然后在模型中为每一个你想使用的实体子集创建配置,这样一来,使用这个模型创建协调器,当你需要添加存储(persistent store)时,可根据不同的配置来指定对应的存储属性。

    通过上面的简单介绍我们已经对Core Data有了初步的了解 下面我们准备从苹果的模板代码体会Core Data是如何具体应用的

    首先是AppDelegate.h

    #import <UIKit/UIKit.h>
    #import <CoreData/CoreData.h>
    
    @interface AppDelegate : UIResponder <UIApplicationDelegate>
    
    @property (strong, nonatomic) UIWindow *window;
    
    @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
    @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
    @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
    
    - (void)saveContext;
    - (NSURL *)applicationDocumentsDirectory;
    
    
    @end

    这里比我们平时创建的项目多了三个属性 就是我们上面介绍过的Core Data 的三个组件 managedObjectContext  managedObjectModel persistentStoreCoordinator

    下面移步.m文件 了解下三个部件的实例化过程

    - (NSManagedObjectContext *)managedObjectContext {
        // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
        if (_managedObjectContext != nil) {
            return _managedObjectContext;
        }
        
        NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
        if (!coordinator) {
            return nil;
        }
        _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
        return _managedObjectContext;
    }

    如果是第一次调用,会实例化一个NSManagedObjectContext对象,并使用persistentStoreCoordinator方法返回的NSPersistentStoreCoordinator对象来配置上下文,最后返回新实例化的NSManagedObjectContext对象。

    然后来看下persistentStoreCoordinator的实例化

    - (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
        // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it.
        if (_persistentStoreCoordinator != nil) {
            return _persistentStoreCoordinator;
        }
        
        // Create the coordinator and store
        
        _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
        NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"ZYCoreData.sqlite"];
        NSError *error = nil;
        NSString *failureReason = @"There was an error creating or loading the application's saved data.";
        if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
            // Report any error we got.
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
            dict[NSLocalizedFailureReasonErrorKey] = failureReason;
            dict[NSUnderlyingErrorKey] = error;
            error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
            // Replace this with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
        
        return _persistentStoreCoordinator;
    }

    同样的如果是第一次调用会实例化一个NSPersistentStoreCoordinator对象,这里要访问到documents目录中的SQLite存储文件ZYCoreData.sqlite,还定义了一个applicationDocumentsDirectory方法,它的作用是获取程序documents的路径

    - (NSURL *)applicationDocumentsDirectory {
        // The directory the application uses to store the Core Data store file. This code uses a directory named "com.maxjia.ZYCoreData" in the application's documents directory.
        return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    }

    ZYCoreData.sqlite文件是在编译ZYCoreData.xcdatamodeld时生成的。 NSPersistentStoreCoordinator初始化时需要传入managedObjectModelNSPersistentStoreCoordinator对象在添加持久存储的时候不仅需要传入存储类型,存储文件URL,选项以及错误类型。如果添加存储的时候出现错误,就会进入if判断,进行错误处理。

    然后是managedObjectModel

    - (NSManagedObjectModel *)managedObjectModel {
        // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
        if (_managedObjectModel != nil) {
            return _managedObjectModel;
        }
        NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"ZYCoreData" withExtension:@"momd"];
        _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
        return _managedObjectModel;
    }

    NSManagedObjectModel类在初始化的时候用到了ZYCoreData.momd文件,这个文件和上面ZYCoreData.sqlite一样是编译项目时,由ZYCoreData.xcdatamodeld数据模型生成,并且保存到app的Bundle目录

    - (void)applicationWillTerminate:(UIApplication *)application {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
        // Saves changes in the application's managed object context before the application terminates.
        [self saveContext];
    }
    - (void)saveContext {
        NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
        if (managedObjectContext != nil) {
            NSError *error = nil;
            if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
                // Replace this implementation with code to handle the error appropriately.
                // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
                abort();
            }
        }
    }

    managedObjectContext对象中的数据的修改,发生在内存中的,需要调用save方法来保存到存储文件当中才能做到数据的持久化,在这里选择在程序退出时调用saveContext保存数据

    部件介绍完了  看看我们这个程序干了什么。首先在程序初始化的时候

     UINavigationController *masterNavigationController = splitViewController.viewControllers[0];
    MasterViewController
    *controller = (MasterViewController *)masterNavigationController.topViewController;
    controller.managedObjectContext
    = self.managedObjectContext; return YES;

    MasterViewController中传入了managedObjectContext MasterViewController继承自UITableViewController 所以我们运行程序  可以看到这个程序实现了 点击左上角加号  把当前时间标签加入到tableView中的功能  多添加几条并关闭程序从新打开  发现之前的数据仍然存在  说明应用做了数据的持久化处理  下面我们看下实现代码

    首先是UITableViewDataSource的相关代码

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        return [[self.fetchedResultsController sections] count];
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
        return [sectionInfo numberOfObjects];
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
        [self configureCell:cell atIndexPath:indexPath];
        return cell;
    }
    
    - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
        // Return NO if you do not want the specified item to be editable.
        return YES;
    }
    
    - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
        if (editingStyle == UITableViewCellEditingStyleDelete) {
            NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
            [context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
                
            NSError *error = nil;
            if (![context save:&error]) {
                // Replace this implementation with code to handle the error appropriately.
                // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
                abort();
            }
        }
    }
    
    - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
        NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
        cell.textLabel.text = [[object valueForKey:@"timeStamp"] description];
    }

    通过代码  我们发现就可看出tableView的数据是由fetchedResultsController对象提供的。所以我们继续来看看fetchedResultsController是何方神圣

    - (NSFetchedResultsController *)fetchedResultsController
    {
        if (_fetchedResultsController != nil) {
            return _fetchedResultsController;
        }
        
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        // Edit the entity name as appropriate.
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:self.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];
    
        [fetchRequest setSortDescriptors:@[sortDescriptor]];
        
        // 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:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"];
        aFetchedResultsController.delegate = self;
        self.fetchedResultsController = aFetchedResultsController;
        
        NSError *error = nil;
        if (![self.fetchedResultsController performFetch:&error]) {
             // Replace this implementation with code to handle the error appropriately.
             // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
        
        return _fetchedResultsController;
    }  

    Core Data在iOS上使用了NSFetchedResultsController对象来简化对提取结果和表格视图的处理。NSFetchedResultsController对象被惰性创建并只在表格视图数据源方法有需要时才提取数据。我们可以看到在对NSFetchRequest对象的处理中,使用了Event实体,并提供了一个NSSortDescriptor对象以让提取结果按timeStamp进行排序。最后通过NSFetchRequest对象和managedObjectContext作为参数传入NSFetchedResultsController的初始化方法。
    NSFetchedResultsController也有它的代理,将MasterViewController设置为其代理,这样在fetched results 发生变化时,MasterViewController中实现的NSFetchedResultsControllerDelegate方法会被调用,然后就可以实现在数据变化是改变tableView中的显示内容

    上面是用NSFetchedResultsController批量获取数据的方法 下面看下数据插入的操作

    - (void)insertNewObject:(id)sender {
        NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
        NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
        NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
            
        // If appropriate, configure the new managed object.
        // Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
        [newManagedObject setValue:[NSDate date] forKey:@"timeStamp"];
            
        // Save the context.
        NSError *error = nil;
        if (![context save:&error]) {
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }

    插入数据需要调用实体描述对象NSEntityDescription返回一个实体对象,然后设置对象属性,最后保存当前上下文即可。这里需要注意,增、删、改操作完最后必须调用管理对象上下文的保存方法,也就是这里[context save:&error]方法

    ,否则不能被执行。

    - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
        if (editingStyle == UITableViewCellEditingStyleDelete) {
            NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
            [context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
                
            NSError *error = nil;
            if (![context save:&error]) {
                // Replace this implementation with code to handle the error appropriately.
                // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
                abort();
            }
        }
    }

    tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath实现了删除操作 拿到上下文  直接调用管理对象上下文的deleteObject方法,删除完保存上下文即可

    总结

    Core Data框架基本的5个类:NSPersistentStoreCoordinator、NSManagedObjectContext、NSManagedObjectModel、NSEntityDescription、NSManagedObject。

    NSPersistentStoreCoordinator持久化存储协调器:负责从文件加载数据和将数据写入文件。

    NSEntityDescription实体描述:实体可以被看做是NSManagedObject对象的一个具体的实现。

    NSManagedObjectContext 管理对象上下文:上下文是内存中的一块暂存区域。查询,插入,删除,修改等操作都是在上下文中进行。在上下文没有保存之前,对数据的任何修改都只记录在暂存区中,不会影响文件内的数据。

    NSManagedObject 管理对象:Core Data的核心单元。模型对象的数据被持有在NSManagedObject对象中。每一个NSManagedObject对象都对应一个实体

    NSManagedObjectModel 对象模型:NSManagedObjectModel通常被定义在一个.mom文件中,文件中保存了所有实体的定义。

  • 相关阅读:
    vue中使用vw适配移动端
    在vue项目中使用scss
    前端用vue怎么接收并导出文件
    QT编辑
    前置声明
    morphologyEx() getStructuringElement()
    # 类定义中调用另一个类函数的方式
    指针
    NEW
    DECLARE_DYNAMIC IMPLEMENT_DYNAMIC
  • 原文地址:https://www.cnblogs.com/zyi1992/p/4839339.html
Copyright © 2020-2023  润新知