CoreData 框架简介(一)
这是该系列文章的第一篇,总体上介绍一下CoreData,目的是让我们对其概念和使用有个初步的印象和了解,由于大部分都是文字内容,看起来可能会比较枯燥,但是还是建议大家努力看完。
iOS3.0引入的CoreData,用于本地存储和检索持久化数据;CoreData是一个功能强大的对象数据库,提供了强大的数据存储和管理能力。
CoreData能将对象映射到关系数据库,让你在编写对象业务逻辑时,无需创建数据库,也不用编写持久化逻辑,专注于应用需求;
下面是CoreData提供的一些主要功能:
1)使用可视化模型编辑器进行数据对象建模;
2)处理对象架构(schema)变更的自动和手动迁移工具;
3)在对象之间建立一对一、一对多和多对多关系;
4)以不同的格式将数据存储到独立的文件中;
5)对象特性(attribute)验证;
6)数据查询和排序;
7)延迟加载数据;
8)与iOS表视图紧密协作;
9)使用提交和撤销功能管理相关对象的变更;
看起来可能有些不太好理解,但是当您对比数据库的使用时,这些优点就会显现;
1 判断是否适合使用CoreData
iOS中的数据持久化方式有很多中:
1)NSUserDefault:
这种方式通常用于保存应用的首选项;
很像使用键值存储的NSDictionary,可存储NSNumber,NSString,NSDate,NSData,NSDictionary,NSArray表示的值,还可存储任何遵守了NSCoding协议的对象,如果使用键值对,字典或数组可以满足应用的持久化需求,就可以使用NSUserDefault。
2)属性列表(plist):
NSDictionary和NSArray都支持读写用户定义的属性列表文件,这种方式采用xml格式,可存储NSNumber,NSString,NSDate,NSData,NSDictionary和NSArray;如果使用字典或数组可满足应用的持久化需求,就可以使用属性列表文件。
3)编码器(Coder)和键式归档(Keyed Archive):
NSCoder和NSKeyedArchiver支持将任何对象存储到二进制文件中;要使用这种持久化方式,要存储的每个自定义对象必须实现NSCoder定义的方法,而开发人员必须负责保存和加载;如果只需要几个自定义对象就能满足应用的持久化需求,就可以使用这种方式。
4)直接使用SQLite:
应用可以使用C语言库libsqlite直接与SQLite数据库交互;SQLite是一种嵌入式关系数据库,不需要服务器,支持SQL92描述的大部分标准SQL语言;在iOS应用中,使用SQLite可实现SQL支持的任何数据持久化逻辑,如定义数据库表和关系,插入数据,查询数据以及更新和删除数据;这种方式的缺点是,应用需要在对象和SQL文件之间建立映射,需要编写检索和保存数据的SQL查询,还需编写代码来跟踪保存的对象。
5)CoreData:
提供了直接使用SQLite的大部分灵活性,同时应用无需关系数据库使用机制;如果应用需要的数据很多,需要维护对象之间的关系或需要快速而轻松的访问特定对象,CoreData可能是不错的选择。
使用CoreData时,应用中通常需要用到的类:
托管对象(Managed object)是NSManagedObject实例,用于存储数据;
托管对象上下文(NSManagedObjectContext)是一个工作区,应用用它来创建、查询和保存托管对象;
2 CoreData托管对象
描述下CoreData的托管对象:
托管对象是什么;
如何在对象模型中定义它们;
应用如何与托管对象交互;
以及在托管对象的定义发生变化时如何应对;
2.1 托管对象
托管对象是NSManagedObject实例,应用主要与之交互;可将托管对象视为字典,包含一组已知的键和对象类型(如字符串或数组),类似这样:
NSString * movieName = [myMovie valueForKey:@"movieName"];
要更新特性,可使用setValue:forKey。
[myMovie setValue:@"Casablanca" forKey:@"movieName"];
可将托管对象定义为NSManagedObject的子类,这样就可以像访问属性那样访问特性;
NSManagedObject子类可以包含自定义方法,多用于功能性的辅助;
可在托管对象之间建立关系,比如在跟踪电影收藏中,被跟踪的电影是否借给了朋友可能有用,为此,可创建Movie和Friend,并建立二者之间关系;
可以是一对一,也可是一对多(比如一个人借了多部电影):
[myMovie setValue:myFriend forKey:@"lentToFriend"];
[myOtherMovie setValue:myFriend forKey:@"lentToFriend"];
NSSet * borrowedMovies = [myFriend valueForKey:@"borrowedMovies"]
这是一对多的例子,将多部电影借给某人,实际上是为关系(lentToFriend和borrwoedMovies是Inverse关系)对应的属性(Friend和NSSet)赋值和取值的过程;
2.2 托管对象模型
托管对象模型(NSManagerdObjectModel)中定义的;托管对象模型包含了一系列实体,实体的特性,特性和实体的有效性约束以及实体之间的关系;
托管对象模型通常是使用Xcode可视化模型编辑器创建的;如果创建项目时选择use coredata,会自动创建一个xxx.xcdatamodeld的文件,也可以手动创建,这个就是可视化模型编辑器;
上图中显示了Movie(实体)的特性以及相应的数据类型;
CoreData支持数据类型如下:
数据类型 | 对应的OC数据类型 |
Integer 16 | NSNumber |
Integer 32 | NSNumber |
Integer 64 | NSNumber |
Decimal | NSNumber |
Double | NSNumber |
Float | NSNumber |
String | NSString |
Boolean | NSNumber |
Date | NSDate |
Binary Data | NSData |
Transformable | 使用值转换器(NSValueTransformer) |
CoreData支持实体继承(上图中Parent Entity),对于任何实体,都可以指定一个父实体,子实体将继承父实体的所有特征,包括特性、有效性约束和索引。
2.3 托管对象迁移
如果需要修改对象模型(即便给实体添加一个特性,也被视为修改了对象模型),就需要对模型进行版本控制;
加载既有的持久存储区时(稍后我会给出一张图,方便大家理解啥时持久存储区),CoreData将对其进行检查,确保他与对象模型匹配,如果不匹配,就需要根据新的对象模型将持久存储区的数据迁移到新的持久存储区;
CoreData能够根据持久存储协调器(稍后看图)中设置的选项自动处理众多的简单迁移,如果需要额外的逻辑,可能还要编写迁移;
这块内容比较复杂,在实践之后,我会在后续的blog中单独写一篇文章来学习。
2.4 创建托管对象
托管对象只能存在于托管对象上下文(NSManagedObjectContext)中,而托管对象上下文是CoreData的工作区;托管对象要么是在托管对象上下文中创建的,要么是从托管对象上下文获取的;要创建托管对象,需要一个指向托管对象上下文的引用;然后需要指定托管对象的实体,在CoreData框架中,又一个NSEntityDescription的类,提供了有关实体的信息(可使用其类方法创建);
NSManagedObject * moc = kAPPDelegate.managedObjectContext;//应用委托设置了一个用于存储上下文的属性
NSManagedObject * newObject = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:moc];//指定托管对象的实体
有了托管对象,就可以更新其相关特性,设置好之后,需要保存托管对象上下文,以持久化托管对象:
NSError * mocSaveError = nil;
if(![moc save:&mocSaveError]){
}
要撤销不想要的修改,可以回滚托管对象上下文;
if([moc hasChanges]){
[moc rollback];
}
这里给出一张图,方便大家理解上述几个重要概念的关系:
2.5 对象检索和排序
1 直接使用objectID:
NSManagedObject * myObject = [kAppDelegate.managedObjectContext objectWithID:myObjectID];
如果没有找到则返回nil;找到了将返回一个NSManagedObject;
2 编写检索请求:
NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription * entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:moc];
[fetchReuqest setEntify:entity];
NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"sortAttributeName" ascending:YES];
NSArray * sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
检索请求还可以使用谓词(NSPredicate),他指定了检索结果必须满足的条件;谓词在后续使用中会详细介绍,如下是简单示例:
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"attribute == %@",@"value"];
[fetchRequest setPredicate:predicate];
检索请求返回的是一个数组,包含0~多个对象;
2.6 检索结果控制器
3 CoreData环境
3.1 持久存储协调器
3.2 持久存储区
3.3 托管对象上下文
4 小结