详解持久化Core Data框架的原理以及使用---转自Bison的技术博客

1.原理部分

Core Data是一个纯粹的面向对象框架,可用于管理实体以及实体之间的关联关系的持久化,也就是我们通常所指的数据持久化。
Core Data底层的持久化存储方式可以是SQLite数据库,也可以是XML文档,甚至可以直接以内存作为持久化存储设备。
Core Data的核心概念是实体。实体是由Core Data管理的模型对象,它必须是NSManagedObject类或其子类的实例。实体与实体之间存在1-1、1-N、N-N、的关联关系,整个应用的所有实体以及实体之间的关联关系被称为托管对象模型NSManagedObiectModel
Core Data的核心对象是托管对象上下文NSManagedObjectContext,所有实体都处于托管对象上下文管理中,Core Data应用对实体所做的任何增、删、查、改操作都必须通过托管对象上下文来完成。
开发者开发的应用程序需要通过NSMannagedObjectContext对实体进行增、删、查、改操作,而NSMannagedObjectContext底层与持久化存储协调衔接,持久化存储协调器则负责管理底层的存储形式比如:SQLite
Core Data应用中的核心API有如下几个。

托管对象模型NSManagedObiectModel:该对象负责管理整个应用的所有实体以及实体之间的关联关系。当开发者使用Xcode的图形界面设计了实体与实体的关联关系之后,需要使用该对象来加载、管理应用的托管对象模型。


持久化存储协调器NSPeristentStoreCoordinator:负责管理底层的存储文件,例如SQLite数据库等。


托管对象上下文NSManagedObjectContext:该对象是Core Data的核心对象,应用对实体所做的任何增、删、查、改操作都必须通过该对象来完成。


实体描述NSEntityDescription:该对象代表了关于某个实体的描述信息,从某种程度来说,该对象相当于实体的抽象。实体描述定义了该实体的名字、实体的实现类,并用一个集合定义了该实体包含的所有属性。


抓取请求NSFetchRequest:该对象封装了查询实体的请求,包括程序需要查询哪些实体、查询条件、排序规则等。抓取请求定义了本次查询的实体的名字、抓取请求的查询条件,通过NSPredicate来表示,并用一个NSArray集合定义了所有的排序规则。


熟悉以上几点之后,使用Core Data持久化操作的步骤大致如下。

  • 创建NSManagedObiectModel对象来加载管理应用的托管对象模型。
  • NSManagedObiectModel对象为基础,根据实际需要创建NSPeristentStoreCoordinator对象,该对象确定Core Data底层数据的存储形式。
  • NSManagedObiectModel对象为基础,创建NSMannagedObjectContext,该对象是Core Data进行持久化访问的核心对象。
  • 对于普通的增、删、查、改操作,需要分别先创建实体、删除实体、修改实体,然后调用NSMannagedObjectContext对象的save:方法将这些修改保存到底层存储设备。
  • 如果要执行查询,则需要先创建NSFetchRequest对象,再调用NSMannagedObjectContextexecuteFetchRequest:error:方法执行查询,该方法返回所有匹配条件的实体组成的NSArray

2.手动配置环境

iOS允许在创建项目时勾选“Use Core Data”复选框,通过该方式创建的项目已经完成了所有Core Data必须资源的初始化,但此处并不打算使用这种方式来初始化Core Data项目,而是选择从一个Empty Application开始,手动初始化Core Data项目,这样便于大家真正理解Core Data项目需要初始化哪些资源。
下面我们将开始把这个Empty Application项目一步步改造成Core Data项目。
具体步骤如下:

  • 为该项目添加CoreData.framework框架。

  • 为该项目添加一个实体模型文件。单击Xcode主菜单的"File"--->"New"--->"File"菜单项,具体如下图:


    coreData01.png
  • AppDelegate中初始化Core Data应用必须的核心API对象:NSManagedObiectModelNSPeristentStoreCoordinatorNSManagedObjectContext修改应用程序委托类的接口部分,在接口部分定义上面3个核心API的属性,并增加一个对NSManagedObjectContext对象执行存储的方法、一个获取应用Docouments目录下的方法。下面是修改后的接口部分代码。

AppDelegate.h

#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
// 定义Core Data的3个核心API的属性
@property (readonly, strong, nonatomic) NSManagedObjectContext*
managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator*
persistentStoreCoordinator;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
@end

接下来在AppDelegate.m中实现部分进行修改,初始化对象,并实现saveContext方法,其中applicationDocumentsDirectory是一个非常简单的方法,用于获取应用Docouments目录
AppDelegate.m

@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    return YES;
}
- (void)applicationWillTerminate:(UIApplication *)application
{
    // 当应用被中断时候,将所有托管上下文中数据保存起来
    [self saveContext];
}

- (void)saveContext
{
    NSError *error = nil;
    // 获取应用的托管对象上下文
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    if (managedObjectContext != nil)
    {
        // 如果托管对象上下文中包含了未保存的修改,执行保存,如果保存失败记录错误信息
        if ([managedObjectContext hasChanges] &&
            ![managedObjectContext save:&error])
        {
            NSLog(@"保存出现错误:%@, %@", error, [error userInfo]);
            abort();
        }
    }
}
// 初始化应用的托管对象上下文。
- (NSManagedObjectContext *)managedObjectContext
{
    // 如果_managedObjectContext已经被初始化过,直接返回该对象   
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }
    // 获取持久化存储协调器
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    // 如果持久化存储协调器不为nil
    if (coordinator != nil)
    {
        // 创建NSManagedObjectContext对象
        _managedObjectContext = [[NSManagedObjectContext alloc] init];
        // 为NSManagedObjectContext对象设置持久化存储协调器
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _managedObjectContext;
}
- (NSManagedObjectModel *)managedObjectModel
{
    // 如果_managedObjectModel已经被初始化过,直接返回该对象
    if (_managedObjectModel != nil) {
        return _managedObjectModel;
    }
    // 获取实体模型文件对应的NSURL
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"FKModel"
        withExtension:@"momd"];
    // 加载应用的实体模型文件,并初始化NSManagedObjectModel对象
    _managedObjectModel = [[NSManagedObjectModel alloc]
        initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    // 如果_persistentStoreCoordinator已经被初始化过,直接返回该对象
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
    // 获取SQLite数据库文件的存储目录
    NSURL *storeURL = [[self applicationDocumentsDirectory]
        URLByAppendingPathComponent:@"Books.sqlite"];
    NSError *error = nil;
    // 以持久化对象模型为基础,创建NSPersistentStoreCoordinator对象
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
        initWithManagedObjectModel:[self managedObjectModel]];
    // 设置持久化存储协调器底层采用SQLite存储机制,如果设置失败记录错误信息
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
        configuration:nil URL:storeURL options:nil error:&error])
    {
        NSLog(@"设置持久化存储失败:%@, %@", error, [error userInfo]);
        abort();
    }
    return _persistentStoreCoordinator;
}
// 获取应用的Documents目录
- (NSURL *)applicationDocumentsDirectory
{
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
        inDomains:NSUserDomainMask] lastObject];
}

到此Core Data所依赖的环境基本上已经搭建完毕,应用执行增、删、查、改操作直接调用managedObjectContext方法即可操作。

3.设计实体模型

Xcode中找到我们创建的Core Data文件,打开实体模型开始编辑,具体如下图:

coredata02.png

下面我们添加一个简单的实体,点击上图走下角的Add Entity,系统将会在ENTITIES列表下添加一个实体,将该实体重命名为项目所需的名字,此处为Bison

PS长按Add Entity将会显示Add EntityAdd Fetch RequestAdd Configuration列表,可供选择添加实体、抓取请求、配置。

选中Bison实体,通过点击上图右下角的Add Attribute按钮,系统将会为该Bison实体添加一个属性,将该属性重命名所需的名字,此处重命名name,并为该属性选择类型,在此选String类型,注意:此处命名首字母不能大写哦。

PS长按Add Attribute将会显示Add AttributeAdd RelationshipAdd Configuration、Add Fetch Property列表,可供选择添加属性、关联关系、抓取属性,也可以通过 +来添加与删除。

重复上面的操作增加一个birthDay的属性,改属性为date类型。实体设计完后,如下图:

coredata03.png

然后单击Xcode主菜单的Editor-->Create NSManagedObject Subclass菜单项,如果系统包含俩个以上的实体,Xcode将会弹出一个对话框让选择腰围哪些实体生成NSManagedObject的子类,然后弹出对话框让选择NSManagedObject的子类的存储路径。选完之后Create按钮即可。
经过上面的操作,为实体模型添加一个简单的Bison实体,该实体包含俩个属性,单不包含任何关联关系。

4.Core Data数据的增删查改

获取托管对象上下文NSManagedObjectContext之后,接下来即可通过该对象来执行增、删、查、改操作。

  • 添加实体
    添加实体的步骤如下:
    调用NSEntityDescriptioninsertNewObjectForEntityForName:inManagedObjectContext:静态方法添加新实体。该方法的第1个参数为实体名,第2个参数为NSManagedObjectContext对象。为新实体设置属性。调用NSManagedObjectContext对象的save:方法执行保存。如下代码片段:
// 控制Core Data在托管对象上下文中创建一个新实体
    Bison* bison = [NSEntityDescription
                      insertNewObjectForEntityForName:@"Bison"
                      inManagedObjectContext:self.appDelegate.managedObjectContext];
    // 为新实体设置属性
    bison.name = @"linbin";
    bison.birth_Day = [NSDate date];
    // 定义一个NSError对象,用于接受错误信息
    NSError *error;
    // 设置完实体属性之后,调用托管对象上下文的`save:`方法将实体写入数据库,如果保存成功
    if ([self.appDelegate.managedObjectContext save:&error])
    {
        [[[UIActionSheet alloc] initWithTitle:@"保存成功" delegate:nil
                            cancelButtonTitle:@"确定" destructiveButtonTitle:nil
                            otherButtonTitles: nil] showInView:self.view];
    }
    else
    {
        NSLog(@"保存FKEvent实体出错: %@,%@" , error ,[error userInfo]);
    }
  • 删除实体
    删除实体的步骤如下:
    首先获取要删除的实体,然后调用NSManagedObjectContext对象的deleteObject:方法删除实体。最后调用NSManagedObjectContext对象的save:方法执行保存。如下代码:
// 获取将要删除的实体
    Bison* deleteEvent = .....;
    // 从托管对象上下文中删除指定对象
    [self.appDelegate.managedObjectContext deleteObject:deleteEvent];
    NSError *error;
    // 保存删除操作,如果出现错误,显示错误信息
    if (![self.appDelegate.managedObjectContext save:&error])
    {
        NSLog(@"删除FKEvent实体出错:%@,%@",error,[error userInfo]);
    }
  • 修改实体
    修改实体的步骤如下:
    首先获取要修改的实体,必须处于NSManagedObjectContext管理下的实体;然后修改实体的属性;再调用NSManagedObjectContext对象的save:方法执行保存。如下代码:
// 获取将要修改的实体
    Bison* updateEvent = .....;
    //修改实体的属性
    .....
    //定义一个NSError对象,用于接收错误信息
    NSError *error;
    // 保存修改操作,如果出现错误,显示错误信息
    if (![self.appDelegate.managedObjectContext save:&error])
    {
        NSLog(@"删除FKEvent实体出错:%@,%@",error,[error userInfo]);
    }
  • 查询实体
    查询实体的步骤如下:
    首先创建NSFetchRequest对象。然后通过NSEntityDescription对象设置NSFetchRequest对象将要抓取的实体。如果需要对抓取结果进行筛选,则需要通过NSPredicate对象设置筛选条件。如果需要对结果进行排序,还需要为NSFetchRequest添加多个NSSortDescriptor对象。再调用NSManagedObjectContext对象的executeFetchRequest:error:方法执行查询,该查询方法将会返回所有符合条件的实体组成的NSArray集合。如下代码:
// 创建抓取数据的请求对象
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    // 设置要抓取哪种类型的实体
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Bison"
        inManagedObjectContext:self.appDelegate.managedObjectContext];
    // 设置抓取实体
    [request setEntity:entity];
    NSError *error = nil;
    // 执行抓取数据的请求,返回符合条件的数据
    eventArray = [[self.appDelegate.managedObjectContext
        executeFetchRequest:request error:&error] mutableCopy];

基本的方法到此就结束了,具体的详情可以下载Demo查看
下载地址

推荐一款学习iOS开发的app_____|______| | 传送门

好文推荐:仿window阿里旺旺登陆界面,打印机吐纸动画效果

原文地址:http://allluckly.cn

如对你有帮助,请不要吝惜你的star和喜欢哦!

技术交流群:534926022(免费) 511040024(0.8/人付费)

版权归©Bison所有 如需转载请保留原文超链接地址!否则后果自负!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容