iOS 通过CoreData实现数据持久化引言:
Core Data 是 iOS 3.0 以后引入的数据持久化解决方案,其原理是对 SQLite 的封装,是开发者不需要接触 SQL 语句,就能够对数据库进行的操作.
其编码方式和原理结构方面较为特殊,本博文主要介绍在使用 Core Data 时遇到的各种问题和对其核心原理进行解释.
参考资料:
1: iOS 教程:Core Data
数据持久性存储基础教程
http://www.dasheyin.com/ios_jiao_cheng_core_data_shu_ju_chi_jiu_xing_cun_chu_ji_chu_jiao_cheng.html
安装:
安装的方式只有1步,引入
CoreData.framework
便可.
使用:
使用 Core Data 起步最早要了解和熟习的类是以下3个:1:NSManagedObjectModel2:NSPersistentStoreCoordinator3:NSManagedObjectContext
在此也特别的说明1下,如果你没有理解透这3个类分别是做甚么的,那末往后看到的代码都有1种非常迷茫的感觉:
接下来分别介绍每个类的具体功能和用处:1.NSManagedObjectModel(管理对象模型,以下简称:上下文):
构建全部数据库的表结构,表字段类型,表与表之间的关系(Relationship)等等凡是和数据结构有关的定义都通过此类来管理.
那末使用此类需要1个 Data Model(数据模型)文件来配合其1起使用,以下图所示新建出来:
那末我们所有数据结构的定义和设计都用这个 Data Model 来完成.在代码方面需要通过文件路径的方式找到它,并初始化 NSManagedObjectModel[csharp] view plaincopy
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Data Model Name" withExtension:@"momd"];
self.keyManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
注:新建Data Model文件以后的文件扩大名称是: Data ModelName.xcdatamodeld但通过XCode编译打包成App以后,其会被转换成1个Data ModelName.momd文件.而我们真正要加的模型文件就是这个Data Model Name.momd文件.
2.NSPersistentStoreCoordinator(持久性数据调和器):
NSPersistentStoreCoordinator 是真正意义上和 SQLite 打交道的类,主要根据NSManagedObjectModel 履行表结构的建立,通过 NSManagedObjectContext 的命令履行数据交互.
[csharp] view plaincopy
1. self.keyPersistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: self.keyManagedObjectModel];
2.
3.4.5.
6.
7.8.
// handle db upgrade
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
yOption,tion,
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticall
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOp
nil];
if (![self.keyPersistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{}
9.注:通过NSManagedObjectModel初始化,1旦初始化成功,SQLite的DB,就已有了完善的表结构
关系,不过这不是我们关心的重点,继续往下.3.NSManagedObjectContext(管理对象上下文)
NSManagedObjectContext 是我们在开发中主要交互的类,数据的增删改查都通过上下文去触发命令并返回结果. 根据1个 NSPersistentStoreCoordinator 完成初始化
[csharp] view plaincopy
self.keyManagedObjectContext = [[NSManagedObjectContext alloc] init];
[self.keyManagedObjectContext setPersistentStoreCoordinator:self.keyPersistentStoreCoordi
nator];
到此,CoreData的准备工作已完成,其实XCode已有模版可以直接完成CoreData的准备工作,不过对新手来讲,最好还是1步1步来,加以理解,以便出现BUG时,能够及时找到解决方案,贵在理解!
接下来,开始操纵数据!
插入1条?更新1条?删除1条?
熟习 Sql
语句的同学:头脑里立刻会想到:insert
into table , update table , delete table
那末在 CoreData,这3项工作全部通过 save 函数来完成,1个函数完成3件事,CoreData 这么锋利的?
NSPredicate(条件适配器)
NSPredicate
主要为 NSFetchRequest
而服务,提供查询时的各种条件语句,方面过滤出复合业务需
求的数据.
以下先列出 NSPredicate 支持的通配符
1:相等(==) 举例: field == 'value'
2:不相等(!=) 举例: field != 'value'
3:模糊(like) 举例: field like '*value*' 或 field like '?value?' like 使用?表示1个字符,*表示多个字符
4:比较(><<=>=)
举例: field>6
以上 4
种通配符都是字符串直接拼接便可,接下来的通配符在拼接字符串方面较为麻烦,但有相干
代码可以辅助拼接.
5:范围(between)
举例: field between {"6", "10"}可以通过以下代码拼接条件命令:
[csharp]
view plaincopy
NSArray *range = [[NSArray alloc]initWithObjects:@"6",@"10",nil];
NSPredicate *betweenPredicate =[NSPredicate predicateWithFormat:@"field between %@",
range];
NSLog(@"%@",betweenPredicate.predicateFormat);
6:包括(in)
举例: filed IN {"value1", "value2"}可以通过以下代码拼接条件命令:
[csharp]
view plaincopy
NSArray *choice = [[NSArray alloc]initWithObjects:@"value1",@"value2",nil];
NSPredicate *inPredicate =[NSPredicate predicateWithFormat:@"filed in %@", choice];
NSLog(@"%@",inPredicate.predicateFormat);
7:复合(or and not) 举例: filed == "value2" OR filed == "value3"
也能够通过以下代码拼接:[csharp] view plaincopy
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"filed == 'value1' "];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"filed == 'value2' "];
NSArray *predicates = [[NSArray alloc]initWithObjects:predicate1,predicate2,nil];
NSPredicate *andCompoundPredicate =[NSCompoundPredicate orPredicateWithSubpredicat
es:predicates];
在调用 save 函数时我需要注意些甚么?1个对象只属于1个上下文对象,所以不同上下文管辖的对象不允许用1个上下文来调用 save
方法,这只会失败,毛病提示以下:
Illegal attempt to establish a relationship 'xyz' between objects in different contexts解决办法是(参考:StackOverflow):
[csharp]
view plaincopy
NSManagedObject *book = // get a book in one MOC
NSManagedObject *owner = // get an owner in a different MOC
[[owner mutableSetV alueForKey:@"books"] addObject:[owner managedObjectContext:objec
tWithID:[book objectID]]];
Persistent Store Coordinator
(持久性数据调和器): 你可以将这个东西看做是数据库连接库,在这里,你将设置数据存储的名字和位置,和数据存储的时机。
Managed Object Context
(管理数据内容):你可以将这1部份看做是数据的实际内容,这也是全部数据库中对我们而言最重要的部份(这还用说),基本上,插入数据,查询数据,删除数据的工作都在这里完成。
NSFetchRequest* request = [[NSFetchRequest alloc] init];
[request setEntity:entity];[request setResultType:NSManagedObjectIDResultType];[request setFetchBatchSize:20];
NSError* error = nil;
NSArray* items = [context executeFetchRequest:request error:&error];
for (NSManagedObjectID* objectID in items) {
NSManagedObject* object = [context objectWithID:objectID];
...}
countForFetchRequest:error
1:表与表之间关系建立教程http://blog.csdn.net/fengsh998/article/details/8123392
2:针对利用升级和表结构变动时 兼容旧版本的CoreData数据库解决办法.
遇到的问题:
当你将CoreData
加入到工程中,并启动了App1切都运行良好,
可是开发途中修改了CoreData
的 数据结构,比如添加或删除某个字段,或新添加了1张表.此时,再运行App时,发现App直接Crash.
如何解决:这说明CoreData没法做到时时的去修改表结构,但CoreData可以以多个副本的情势来处理数据结构变化时的Crash问题.
论述1下原理:
原理类似SVN
需要打1个
tag 1样,
1担打了Tag就意味着这个版本的代码将不再允许修改,如果需要修改,需要到新的分枝里去实现.当利用开发工具新建CoreData
管理文件以后:FEMicroCoopModel.xcdatamodeld默许是只有1个分枝的.
那末添加分枝方式以下:
1.IDE->Editor->Add Model Version...
2.以后显示以下界面:
两个字段:
Version name:版本名称(按你所需来取)
Based on model:基础模型(这里选择1个,已有的分枝,继承的概念)
Finish以后就完成了,那末新的数据结构修改,都请在这个文件上面进行操作.
当你修改的差不多以后,需要设置 CoreData管理文件的 (Versioned Core Data Model)当前使用版本,以下图:
只有这样利用运行时才会依照新版数据结构去迁移数据和修改表结构.
代码方面只有两个地方需要注意1下:1:添加对数据结构版本自适应的配置,代码以下:[csharp] view plaincopy
// handle db upgrade
NSDictionary *options =
[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
nil];
2:在实例化 NSManagedObjectModel 对象 需要传入模型名称,这里只要是你当初建立CoreData管理对象时的名称便可.
3:让控制台具有输出 Core Data履行的SQL语句的能力.为Edit Scheme - Run - Arguments - 添加1项值:[csharp] view plaincopy
1. -com.apple.CoreData.SQLDebug 1
以下图所示:
3:正告和错误1:has no children正告提示以下图:
解决方法:
将图中的勾勾去掉 编译便可