iOS开发数据库SQLite的使用介绍了iOS中使用SQLite对数据进行持久化存储,实际上是对数据库直接进行操作,而苹果专门有一套API来间接的对数据进行持久化存储,而且主要针对用户创建的对象 — Core Data

Core Data有很多强大的功能,使用图形化界面来创建对象。可以使用NSPredicate对数据进行筛选,使用NSSortDescriptor对数据进行排序。还可以通过获取属性与请求模板的设置筛选数据。也可以建立对象与对象之间的关联关系,一对一,一对多,多对多种情况。对于每个对象的属性也可以指定约束和验证的规则。而且苹果还对Core Data的性能做了优化,如添加对查询数据的缓存,关联对象不会立刻访问达到节省内存的目的等。Core Data 还提供了Mapping Model来解决数据库版本升级的问题。

本篇主要介绍Core Data的架构与Core Data使用的基础配置。

Core Data架构

很多人刚刚接触Core Data 时只看一些模板代码与API并不知道Core Data设计架构是什么样的,导致不了解Core Data内部是如何工作以至于学习起来总感觉生涩难懂,了解Core Data的设计架构对学习和理解Core Data是有很大的帮助。首先我们先看看Core Data中的对象以及这些对象间是如何工作的:

数据存储

数据存储是通过持久存储协调器将用户创建的对象写入磁盘中,或者从磁盘中读取用户创建的对象。存储的文件可以是一个数据库文件,可以是一个二进制文件在初始化持久协调器时可以指定存储的类型。

注意: 不要尝试修改存储的文件,如果对使用Core Data存储的文件进行修改,将改变文件结构导致使用Core Data读取文件时出错

持久化存储协调器

持久化存储协调器的是NSPersistentStoreCoordinator类的实例,只是扮演托管对象上下文和数据存储之间的中间人角色,当托管对象上下文进行数据请求时通过持久化存储协调器从存储文件的位置获取数据,而托管上下文存储数据是也是通过是持久化存储协调器进行数据存储。

注意:NSPersistentStoreCoordinator类不是线程安全的类当多个线程需要对数据进行操作时需要给NSPersistentStoreCoordinator类的实例加锁或者在每个线程中重新实例化一个NSPersistentStoreCoordinator类。关于锁的使用请参考我之前写的一篇文章Objective-C中的同步线程的锁

托管对象模型

托管对象模型定义了应用中使用的数据模型的结构,托管对象是NSManagedObjectModel类的实例对象,托管对象模型由一组实体组成每一组实体都表示一类数据结构,可以定义每个模型的结构与各个模型之间的关系。持久化存储协调器根据托管对象模型中定义的数据结构与每个实体之间的关系来创建托管对象,并将这些托管对象存到磁盘中。

托管对象上下文

托管对象上下文是NSManagedObjectContext类的实例,托管对象上下文主要提供一个访问托管对象的接口,管理所有的托管对象。可以在托管对象上下文中对托管对象进行操作,包括添加对象,删除对象,修改对象,查找对象。一般从磁盘中获取数据时都要创建一个请求,指定托管对象上下文然后设置请求的条件来访问数据。


Core Data使用的基础配置

下面通过创建一个带有Core Data模板的项目来介绍Core Data使用时各项参数配置。

新建一个Xcode项目选择Master-Detail Application

点击Next并勾选Use Core Data

此时可以先运行查看一下效果,每次添加都会增加一行显示当前时间的信息,当完全退出应用时再次打开这些信息还存在说明了已经对数据持久化存储了。

AppDelegate里可以看到Core Data相关对象初始化的过程。首先是托管对象模型NSManagedObjectModel的创建

- (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:@"LSYBaseData" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

在应用的项目文件中包含后缀为.xcdatamodeld的文件这个文件就是托管对象模型,在后台Xcode会将这个文件编译为momd类型的文件,momd文件是部署到设备上的文件。

然后是持久化存储协调器NSPersistentStoreCoordinator类的实例创建。

- (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:@"LSYBaseData.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对象,因为NSPersistentStoreCoordinator对象是托管对象模型、托管对象上下文、数据存储的中间人,后面可以看到创建托管对象上下文也需要用到NSPersistentStoreCoordinator对象。

持久化存储协调器指定了存储类型为数据库文件所以通过创建一个.sqlite文件的路径来存储数据。

之后的代码是将数据存储添加到协调器。第一个参数NSSQLiteStoreType说明应该是数据库存储,当然也可以指定其他的存储参数。第二个参数configuration,通过传入configuration的名称将不同的托管对象模型存储到不同的数据文件中,默认将托管对象模型中的所有实体对象都存到同一个文件中。第三个参数options通过传入一个字典来添加对数据存储的附加配置,后面添加模型映射时会添加此配置。此时不需要所以传nil。

最后一段代码是创建一个托管对象的上下文

- (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;
}

通过一个上面创建的持久化存储协调器来配置一个托管对象上下文。

现在Core Data 基本对象都已经初始化那么在MasterViewController.m文件中查看Core Data是如果对数据进行操作的。

首先Core Data想要获取数据必须构建一个请求。然后请求结果的控制器根据构建的请求和托管对象上下文来获取数据。请求结果控制器通过与托管对象上下文绑定,每当上下文数据发生变化时,请求结果控制器都会调用相应的代理方法。

下面查看初始化请求结果控制器的相关代码

- (NSFetchedResultsController *)fetchedResultsController
{
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];  // 初始化请求
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:self.managedObjectContext];    //获取实体描述
    [fetchRequest setEntity:entity];

    [fetchRequest setFetchBatchSize:20];    //设置获取数量

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO];

    [fetchRequest setSortDescriptors:@[sortDescriptor]];    //设置排序条件可以多个条件

    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;
}  

下面是获取请求结果的相关代理方法:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller;

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller;

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

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type;

- (nullable NSString *)controller:(NSFetchedResultsController *)controller sectionIndexTitleForSectionName:(NSString *)sectionName;

然后是添加数据:

- (void)insertNewObject:(id)sender {
    NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext]; //获取托管对象上下文
    NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];    //获取实体名称
    NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context]; //添加托管对象

    [newManagedObject setValue:[NSDate date] forKey:@"timeStamp"];  //设置托管对象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();
    }
}

托管对象更改属性名是通过KVC方式进行修改,如果一旦属性名写错的话就会导致程序崩溃,后面的文章会通过实例化NSManagedObject的子类提供gettersetter方法来修改属性。

删除托管对象是通过托管对象上下文对象提供的方法来删除的,具体实现在UITableView的代理中:

- (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();
        }
    }
}

结束

本篇主要介绍了Core Data架构与使用时的基本配置以及对数据的基本操作。后面一系列文章将介绍Core Data众多的进阶使用。

Core Data系列文章(一)Core Data基础的更多相关文章

  1. 转载: ASP.NET Core入门系列文章

    今天在网上发现了ithome上的asp.net core 系列文章,对于新手入门还不错,这里转载一下,也方便查阅. [Day01] 從頭開始 [Day02] 程式生命週期 (Application L ...

  2. 【.Net Core 学习系列】-- EF Core 实践(Code First)

    一.开发环境: VS2015, .Net Core 1.0.0-preview2-003156 二解决方案: 新建项目: File --> New --> Project -->   ...

  3. 【.Net Core 学习系列】-- EF Core实践(DB First)

    一.开发环境: VS2015, .Net Core 1.0.0-preview2-003156 二.准备数据: CREATE DATABASE [Blogging]; GO USE [Blogging ...

  4. 从零开始学习 asp.net core 2.1 web api 后端api基础框架(三)-创建Data Transfer Object

    原文:从零开始学习 asp.net core 2.1 web api 后端api基础框架(三)-创建Data Transfer Object 版权声明:本文为博主原创文章,未经博主允许不得转载. ht ...

  5. 《基于.NET Core构建微服务》系列文章(更新至第6篇,最新第7篇,已发布主页候选区)

    原文:Building Microservices On .NET Core – Part 1 The Plan 时间:2019年1月14日 作者:Wojciech Suwała, Head Arch ...

  6. 《驾驭Core Data》 第一章 Core Data概述

    <驾驭Core Data>系列教程综合了<Core Data for iOS>,<Learning Core Data for iOS>,<Core Data ...

  7. .NET Core微服务架构学习与实践系列文章索引目录

    一.为啥要总结和收集这个系列? 今年从原来的Team里面被抽出来加入了新的Team,开始做Java微服务的开发工作,接触了Spring Boot, Spring Cloud等技术栈,对微服务这种架构有 ...

  8. .NET Core 微服务学习与实践系列文章目录索引(2019版)

    参考网址: https://archy.blog.csdn.net/article/details/103659692 2018年,我开始学习和实践.NET Core,并开始了微服务的学习,以及通过各 ...

  9. Working with Data » 使用Visual Studio开发ASP.NET Core MVC and Entity Framework Core初学者教程

    原文地址:https://docs.asp.net/en/latest/data/ef-mvc/intro.html The Contoso University sample web applica ...

随机推荐

  1. BZOJ 3144 切糕(最小割)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=3144 题意: 思路:我们假设没有那个D的限制.这样就简 单了.贪心的话,我们只要在每一个 ...

  2. HDU 4870 Rating 概率DP

    Rating Time Limit:5000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Statu ...

  3. Cheatsheet: 2015.02.01 ~ 02.28

    Other API Best Practices: API Management Rewriting History with Git Rebase .NET Announcing Microsoft ...

  4. CA*Layer(CATransformLayer--CAGradientLayer)

    CATransformLayer CATransformLayer不同于普通的CALayer,因为它不能显示它自己的内容.只有当存在了一个能作用域子图层的变换它才真正存在.CATransformLay ...

  5. phpcms 头部搜索栏上边的 “新闻 | 图片 | 下载 | 专题” 是在哪里修改的?

    phpcms 是怎么修改以下 栏目列. 在后台的时候,“模块管理>全站搜索”,可以修改搜索分类

  6. 机器学习十大算法之KNN(K最近邻,k-NearestNeighbor)算法

    机器学习十大算法之KNN算法 前段时间一直在搞tkinter,机器学习荒废了一阵子.如今想重新写一个,发现遇到不少问题,不过最终还是解决了.希望与大家共同进步. 闲话少说,进入正题. KNN算法也称最 ...

  7. MyEclipse中文乱码解决方法

    在Myeclipse导入一个项目,有中文乱码问题,解决方法如下: 一.将整个project设置编码UTF-8(UTF-8可以最大的支持国际化) windows->Preferences-> ...

  8. HDU 1003 Max Sum 解题报告

    题目大意:求一串数字中,几个连续数字加起来最大值,并确定起始和最末的位置. 思路:这是一题DP题,但是可以用尺取法来做.我一开始不会,也是看了某大神的代码,然后有人告诉我这是尺取法,现在会了. //尺 ...

  9. nodejs学习笔记<一>安装及环境搭建

    零零散散学了几天nodejs,进度一直停滞不前,今天沉下心来好好看了下nodejs的介绍和代码.自己也试着玩了下,算是有点入门了. 这里来做个学习笔记. ——————————————————————— ...

  10. iOS开发之真机测试

    profile 位置在  /Users/userName/Library/MobileDevice/Provisioning Profiles /Users/user_lzz/Library/Mobi ...