CoreData(表结构变化处理)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
CoreData(表结构变化处理)
引⾔:
Core Data 是 iOS 3.0 以后引⼊的数据持久化解决⽅案,其原理是对SQLite的封装,是开发者不需要接触SQL语句,就可以对数据库进⾏的操作.
其编码⽅式和原理结构⽅⾯较为特殊,本博⽂主要介绍在使⽤Core Data时遇到的各种问题以及对其核⼼原理进⾏解释.
参考资料:
1: iOS教程:Core Data数据持久性存储基础教程
安装:
安装的⽅式只有⼀步,引⼊CoreData.framework 即可.
使⽤:
使⽤Core Data起步最先要了解和熟悉的类是以下三个:
1:NSManagedObjectModel
2:NSPersistentStoreCoordinator
3:NSManagedObjectContext
在此也特别的说明⼀下,如果你没有理解透这三个类分别是做什么的,那么往后看到的代码都有⼀种⾮常迷茫的感觉:
接下来分别介绍每⼀个类的具体功能和⽤途:
1.NSManagedObjectModel(管理对象模型,以下简称:上下⽂):
构建整个数据库的表结构,表字段类型,表与表之间的关系(Relationship)等等凡是和数据结构有关的定义都通过此类来管理.
那么使⽤此类需要⼀个Data Model(数据模型)⽂件来配合其⼀起使⽤,如下图所⽰新建出来:
那么我们所有数据结构的定义和设计都⽤这个Data Model来完成.
在代码⽅⾯需要通过⽂件路径的⽅式找到它,并初始化NSManagedObjectModel
[csharp]
1. NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Data Model Name" withExtension:@"momd"];
2. self.keyManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
注:新建Data Model⽂件以后的⽂件扩展名称是: Data Model Name.xcdatamodeld但通过XCode编译打包成App以后,其会被转换成⼀个Data Model Name.momd⽂件.⽽我们真正要加的模型⽂件就是这个Data Model Name.momd⽂件.
2.NSPersistentStoreCoordinator(持久性数据协调器):
NSPersistentStoreCoordinator是真正意义上和SQLite打交道的类,主要根据NSManagedObjectModel 执⾏表结构的建⽴,通过NSManagedObjectContext 的命令执⾏数据交互 .
[csharp]
1. self.keyPersistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: self.keyManagedObjectModel];
2.
3. // handle db upgrade
4. NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
5. [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
6. [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
7. nil];
8. if (!
[self.keyPersistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) 9. {}
注:通过NSManagedObjectModel初始化,⼀旦初始化成功,SQLite的DB,就已经有了完善的表结构关系,不过这不是我们关⼼的重点,继续往下.
3.NSManagedObjectContext(管理对象上下⽂)
NSManagedObjectContext是我们在开发中主要交互的类,数据的增删改查都通过上下⽂去触发命令并返回结果. 根据⼀个NSPersistentStoreCoordinator 完成初始化
[csharp]
1. self.keyManagedObjectContext = [[NSManagedObjectContext alloc] init];
2. [self.keyManagedObjectContext setPersistentStoreCoordinator:self.keyPersistentStoreCoordinator];
到此,CoreData的准备⼯作已经完成,其实XCode已经有模版可以直接完成CoreData的准备⼯作,不过对于新⼿来说,最好还是⼀步⼀步来,加以理解,以便出现BUG时,能够及时找到解决⽅案,贵在理解!
接下来,开始操纵数据!
插⼊⼀条?更新⼀条?删除⼀条?
熟悉Sql语句的同学:脑⼦⾥⽴刻会想到:insert into table , update table , delete table
那么在CoreData,这三项⼯作全部通过save函数来完成,⼀个函数完成三件事,CoreData这么犀利的?
NSPredicate(条件适配器)
NSPredicate主要为NSFetchRequest⽽服务,提供查询时的各种条件语句,⽅⾯过滤出复合业务需求的数据.
以下先列出 NSPredicate ⽀持的通配符
1:相等(==) 举例: field == 'value'
2:不相等(!=) 举例: field != 'value'
3:模糊(like) 举例: field like '*value*' 或者 field like '?value?' like 使⽤?表⽰⼀个字符,*表⽰多个字符
4:⽐较( > < <= >= ) 举例: field > 6
以上4种通配符都是字符串直接拼接即可,接下来的通配符在拼接字符串⽅⾯较为⿇烦,但有相关代码可以辅助拼接.
5:范围(between) 举例: field between {"6", "10"}
可以通过如下代码拼接条件命令:
[csharp]
1. NSArray *range = [[NSArray alloc]initWithObjects:@"6",@"10",nil];
2. NSPredicate *betweenPredicate =[NSPredicate predicateWithFormat:@"field between %@", range];
3. NSLog(@"%@",betweenPredicate.predicateFormat);
6:包含(in) 举例: filed IN {"value1", "value2"}
可以通过如下代码拼接条件命令:
[csharp]
1. NSArray *choice = [[NSArray alloc]initWithObjects:@"value1",@"value2",nil];
2. NSPredicate *inPredicate =[NSPredicate predicateWithFormat:@"filed in %@", choice];
3. NSLog(@"%@",inPredicate.predicateFormat);
7:复合(or and not) 举例: filed == "value2" OR filed == "value3"
也可以通过如下代码拼接:
[csharp]
1. NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"filed == 'value1' "];
2. NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"filed == 'value2' "];
3. NSArray *predicates = [[NSArray alloc]initWithObjects:predicate1,predicate2,nil];
4. NSPredicate *andCompoundPredicate =[NSCompoundPredicate orPredicateWithSubpredicates:predicates];
在调⽤save 函数时我需要注意些什么?
⼀个对象只属于⼀个上下⽂对象,所以不同上下⽂管辖的对象不允许⽤⼀个上下⽂来调⽤save ⽅法,这只会失败,错误提⽰如下:
Illegal attempt to establish a relationship 'xyz' between objects in different contexts
解决办法是(参考:):
[csharp]
1. NSManagedObject *book = // get a book in one MOC
2. NSManagedObject *owner = // get an owner in a different MOC
3. [[owner mutableSetValueForKey:@"books"] addObject:[owner managedObjectContext:objectWithID:[book objectID]]];
Persistent Store Coordinator (持久性数据协调器):你可以将这个东西看作是数据库连接库,在这⾥,你将设置数据存储的名字和位置,以及数据存储的时机。
Managed Object Context (管理数据内容):你可以将这⼀部分看作是数据的实际内容,这也是整个数据库中对我们⽽⾔最重要的部分(这还⽤说),基本上,插⼊数据,查询数据,删除数据的⼯作都在这⾥完成。
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:表与表之间关系建⽴教程
2:针对应⽤升级和表结构变动时兼容旧版本的CoreData数据库解决办法.
遇到的问题:
当你将CoreData 加⼊到⼯程中,并启动了App⼀切都运⾏良好,
可是开发途中修改了CoreData 的数据结构,⽐如添加或者删除了某个字段,或者新添加了⼀张表.
此时,再运⾏App时,发现App直接Crash.
如何解决:
这说明CoreData⽆法做到时时的去修改表结构,但CoreData可以以多个副本的形式来处理数据结构变化时的Crash问题.
阐述⼀下原理: 原理类似SVN 需要打⼀个 tag ⼀样, ⼀担打了Tag 就意味着这个版本的代码将不再允许修改,如果需要修改,需要到新的分枝⾥去实现.当利⽤开发⼯具新建CoreData 管理⽂件以后:FEMicroCoopModel.xcdatamodeld 默认是只有⼀个分枝的.
那么添加分枝⽅式如下:
1.IDE->Editor->Add Model Version...
2.之后显⽰如下界⾯:
两个字段:
Version name:版本名称(按你所需来取)
Based on model:基础模型(这⾥选择⼀个,已经有的分枝,继承的概念)
Finish之后就完成了,那么新的数据结构修改,都请在这个⽂件上⾯进⾏操作.
当你修改的差不多以后,需要设置 CoreData管理⽂件的 (Versioned Core Data Model) 当前使⽤版本,如下图:
只有这样应⽤运⾏时才会按照新版数据结构去迁移数据和修改表结构.
代码⽅⾯只有两个地⽅需要注意⼀下:
1:添加对数据结构版本⾃适应的配置,代码如下:
[csharp]
1. // handle db upgrade
2. NSDictionary *options =
3. [NSDictionary dictionaryWithObjectsAndKeys:
4. [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
5. [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
6. nil];
2:在实例化 NSManagedObjectModel 对象需要传⼊模型名称,这⾥只要是你当初建⽴ CoreData管理对象时的名称即可.
3:让控制台拥有输出 Core Data执⾏的SQL语句的能⼒.
为Edit Scheme - Run - Arguments - 添加⼀项值:
[csharp]
1. -com.apple.CoreData.SQLDebug 1
如下图所⽰:
3:警告和错误
1:has no children
警告提⽰如下图:
解决⽅法:
将图中的勾勾去掉编译即可。