您的位置:首页 > 其它

CoreData的使用/以及coreData中的多线程问题/版本迁移(二)

2017-03-23 09:11 661 查看
CoreData的使用
1.coreData简介
coreData是苹果对sqlite的封装,不用操作sqlite语句,他提供了对象关系映射功能,能将oc对象转化成数据,保存在sqlite中,也能将保存的数据还原成oc对象;
coredata有两种队列:私有队列,主队列
coreData中的主要包括这几个部分:管理对象上下文,数据持久化协调器,模型文件(包含实体,实体对应的是实体类),2.coreData的使用// 1.创建模型文件 [相当于一个数据库里的所有表]
// 2.添加实体(可以添加多个实体) ,添加相应实体的属性[添加一个实体相当于数据库中添加了一张表]Undefined: 默认值,参与编译会报错Integer 16: 整数,表示范围 -32768 ~ 32767Integer 32: 整数,表示范围 -2147483648 ~ 2147483647Integer 64: 整数,表示范围 –9223372036854775808 ~ 9223372036854775807Float: 小数,通过MAXFLOAT宏定义来看,最大值用科学计数法表示是 0x1.fffffep+127fDouble: 小数,小数位比Float更精确,表示范围更大String: 字符串,用NSString表示Boolean: 布尔值,用NSNumber表示Date: 时间,用NSDate表示Binary Data: 二进制,用NSData表示Transformable: OC对象,用id表示。可以在创建托管对象类文件后,手动改为对应的OC类名。使用的前提是,这个OC对象必须遵守并实现NSCoding协议

// 3.创建对应的实体类() [相当于管理对象模型,有一个实体就创建一个实体类,就相当于一个实体模型]



那么对于CoreData, 我们不用直接接触sql语句, 这种表间的联合查询我们应该怎么办呢?


CoreData 的联合查询.
1.我们创建一个
部门
的示例, 请注意 Employee 的 Releationships 部分.这里, 实际上Department做为Employee的外键, 在Employee中有一个字段为depart. 如此设置之后,这两张表已经完成前面我们描述的
表间关联
, 不用出现join关键字, 我们已经将两张表牢牢的绑在一起了.Department实体添加Relationships的操作和Employee都一样,区别在于用红圈标出的Type,这里设置的To Many一对多的关系。这里默认是To One一对一,上面的Employee就是一对一的关系。也就符合一个Department可以有多个Employee,而Employee只能有一个Department的情况,这也是符合常理的。Relationships类似于SQLite的外键,定义了在同一个模型中,实体与实体之间的关系。可以定义为对一关系或对多关系,也可以定义单向或双向的关系,根据需求来确定。如果是对多的关系,默认是使用NSSet集合来存储模型。Inverse是两个实体在Relationships中设置关联关系后,通过设置inverse为对应的实体,这样可以从一个实体找到另一个实体,使两个实体具有双向的关联关系。Fetched Properties在实体最下面,有一个Fetched Properties选项,这个选项用的不多,这里就不细讲了。Fetched Properties用于定义查询操作,和NSFetchRequest功能相同。定义fetchedProperty对象后,可以通过NSManagedObjectModel类的fetchRequestFromTemplateWithName:substitutionVariables:方法或其他相关方法获取这个fetchedProperty对象。fetched Property获取这个对象后,系统会默认将这个对象缓存到一个字典中,缓存之后也可以通过fetchedProperty字典获取fetchedProperty对象。Fetch Requests在模型文件中Entities下面有一个Fetch Requests,这个也是配置请求对象的。但是这个使用起来更加直观,可以很容易的完成一些简单的请求配置。相对于上面讲到的Fetched Properties,这个还是更方便使用一些。Fetch Requests上面是对Employee实体的height属性配置的Fetch Request,这里配置的height要小于2米。配置之后可以通过NSManagedObjectModel类的fetchRequestTemplateForName:方法获取这个请求对象,参数是这个请求配置的名称,也就是EmployeeFR。// 4.生成管理对象上下文 并关联模型文件生成数据库 /* * 关联的时候,如果本地没有数据库文件,Coreadata自己会创建 */ // 创建上下文 NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; // model模型文件 // NSManagedObjectModel *model =[NSManagedObjectModel mergedModelFromBundles:nil];//使用这个方法,如果 bundlesnil会把bundles里面的所有模型文件的表放在一个数据库中 //使用下面这个方法,是把一个模型文件对应一个数据库NSURL *companyURL = [[NSBundle mainBundle]URLForResource:modelName withExtension:@"momd"];NSManagedObjectModel *model = [[NSManagedObjectModel alloc]initWithContentsOfURL:companyURL]; // 持久化存储调度器(通过她把模型链接到本地数据库) // 持久化,把数据保存到一个文件,而不是内存 NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; // 告诉Coredata数据库的名字和路径 NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *sqlitePath = [doc stringByAppendingPathComponent:@"company.sqlite"]; NSLog(@"%@",sqlitePath);//添加持久化存储(也就时设置存储类型和存储路径) [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURLfileURLWithPath:sqlitePath] options:nil error:nil]; //设置上下文的持久化调度器 context.persistentStoreCoordinator = store; _context = context;}==========//使用这个方法是把不同的模型文件的表放到不同的数据库中-(NSManagedObjectContext *)setupContextWithModelName:(NSString*)modelName{// 1.创建模型文件[相当于一个数据库里的表]// 2.添加实体[一张表]// 3.创建实体类 [相当模型]// 4.生成上下文关联模型文件生成数据库/** 关联的时候,如果本地没有数据库文件,Coreadata自己会创建*/// 上下文NSManagedObjectContext *context = [[NSManagedObjectContext alloc]init];// 上下文关连数据库// model模型文件NSURL *companyURL = [[NSBundlemainBundle]URLForResource:modelNamewithExtension:@"momd"];NSManagedObjectModel *model = [[NSManagedObjectModelalloc]initWithContentsOfURL:companyURL];// 持久化存储调度器// 持久化,把数据保存到一个文件,而不是内存NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinatoralloc]initWithManagedObjectModel:model];// 告诉Coredata数据库的名字和路径NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)lastObject];NSString *sqliteName = [NSStringstringWithFormat:@"%@.sqlite",modelName];NSString *sqlitePath = [doc stringByAppendingPathComponent:sqliteName];NSLog(@"%@",sqlitePath);[store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURLfileURLWithPath:sqlitePath] options:nil error:nil];context.persistentStoreCoordinator = store;return context;}==========// 数据库的操作 CURD Create Update Read Delete#pragma mark -----增 // 通过模型文件中的实体创建一个员工对象 //Employee *emp = [[Employee alloc] init]; Employee *emp = [NSEntityDescription insertNewObjectForEntityForName:@"Employee"inManagedObjectContext:_context]; emp.name = @"wangwu"; emp.height = @1.80; emp.birthday = [NSDate date]; // 直接保存数据库 NSError *error = nil; [_context save:&error]; if (error) { NSLog(@"%@",error); }#pragma mark ----查 // 1.FectchRequest 创建抓取请求对象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 2.设置过滤条件 // 查找zhangsan NSPredicate *pre = [NSPredicate predicateWithFormat:@"name
= %@",
@"zhangsan"]; request.predicate = pre; // 3.设置排序 // 身高的升序排序 NSSortDescriptor *heigtSort = [NSSortDescriptor sortDescriptorWithKey:@"height"ascending:NO]; request.sortDescriptors = @[heigtSort]; // 4.执行请求 NSError *error = nil; NSArray *emps = [_context executeFetchRequest:request error:&error]; if (error) { NSLog(@"error"); } //NSLog(@"%@",emps); //遍历员工 for (Employee *emp in emps) { NSLog(@"名字 %@ 身高 %@ 生日 %@",emp.name,emp.height,emp.birthday); }#pragma mark -----改 // 1.查找到zhangsan // 1.1FectchRequest 抓取请求对象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 1.2设置过滤条件 // 查找zhangsan NSPredicate *pre = [NSPredicate predicateWithFormat:@"name
= %@",
@"zhangsan"]; request.predicate = pre; // 1.3执行请求 NSArray *emps = [_context executeFetchRequest:request error:nil]; // 2.更新身高 for (Employee *e in emps) { e.height = @2.0; } // 3.保存 [_context save:nil];#pragma mark ------删 // 1.查找lisi // 1.1FectchRequest 抓取请求对象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 1.2设置过滤条件 // 查找zhangsan NSPredicate *pre = [NSPredicate predicateWithFormat:@"name
= %@",
@"lisi"]; request.predicate = pre; // 1.3执行请求 NSArray *emps = [_context executeFetchRequest:request error:nil]; // 2.删除 for (Employee *e in emps) { [_context deleteObject:e]; } // 3.保存 [_context save:nil];====================================模糊查询:// 1.FectchRequest 抓取请求对象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; //设置身高的升序排序 NSSortDescriptor *heigtSort = [NSSortDescriptor sortDescriptorWithKey:@"height"ascending:NO]; request.sortDescriptors = @[heigtSort]; // 模糊查询 // 名字以"wang"开头// NSPredicate *pre = [NSPredicate predicateWithFormat:@"name BEGINSWITH %@",@"wangwu1"];// request.predicate = pre; // 名字以"1"结尾// NSPredicate *pre = [NSPredicate predicateWithFormat:@"name ENDSWITH %@",@"1"];// request.predicate = pre; // 名字包含"wu1"// NSPredicate *pre = [NSPredicate predicateWithFormat:@"name CONTAINS %@",@"wu1"];// request.predicate = pre; // like //以wangwu1*结尾 NSPredicate *pre = [NSPredicate predicateWithFormat:@"name
like %@",@"*wu12"];
//以wangwu1开头 NSPredicate *pre = [NSPredicate predicateWithFormat:@"name
like %@",@"wu12*"];
//匹配正则表达式NSPredicate *pre = [NSPredicate predicateWithFormat:@"name
like %@",@"正则表达是语句"];request.predicate = pre;// 4.执行请求NSError *error = nil;NSArray *emps = [_context executeFetchRequest:request error:&error];====================================
分页查询// 1.FectchRequest 抓取请求对象NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];// 设置身高的升序排序NSSortDescriptor *heigtSort = [NSSortDescriptorsortDescriptorWithKey:@"height"ascending:NO];request.sortDescriptors = @[heigtSort];// 总有共有15数据// 每次获取6条数据// 第一页 0,6// 第二页 6,6// 第三页 12,6 3条数据// 分页查询 limit 0,5// 分页的起始索引request.fetchOffset = 12;// 分页的条数request.fetchLimit = 6;// 4.执行请求NSError *error = nil;NSArray *emps = [_context executeFetchRequest:requesterror:&error];//执行查询++++++++++++++++++++++++++++直接创建查询结果控制器查询//懒加载-(NSFetchedResultsController *)fetchController{// 创建请求对象,并指明操作Employee表NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"];// 设置排序规则,指明根据height字段升序排序NSSortDescriptor *heightSort = [NSSortDescriptorsortDescriptorWithKey:@"age"ascending:YES];request.sortDescriptors = @[heightSort];// 创建NSFetchedResultsController控制器实例,并绑定MOCNSError *error = nil;NSFetchedResultsController *fetchedResultController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:contextsectionNameKeyPath:@"name"cacheName:nil];// 设置代理,并遵守协议fetchedResultController.delegate = self;// 执行获取请求,执行后FRC会从持久化存储区加载数据,其他地方可以通过FRC获取数据[fetchedResultController performFetch:&error];// 错误处理if (error) {NSLog(@"NSFetchedResultsController init error : %@", error);}return fetchedResultController;}在上面初始化fetchedResultController时,传入的sectionNameKeyPath:参数,是指明当前托管对象的哪个属性当做section的title,在本文中就是Employee表的sectionName字段为section的title。从NSFetchedResultsSectionInfo协议的indexTitle属性获取这个值。在sectionNameKeyPath:设置属性名后,就以这个属性名作为分组title,相同的title会被分到一个section中。初始化fetchedResultController时参数managedObjectContext:传入了一个MOC参数,fetchedResultController只能监测这个传入的MOC发生的本地持久化改变。就像上面介绍时说的,其他MOC对同一个持久化存储区发生的改变,FRC则不能监测到这个变化。再往后面看到cacheName:参数,这个参数我设置的是nil。参数的作用是开启fetchedResultController的缓存,对获取的数据进行缓存并指定一个名字。可以通过调用deleteCacheWithName:方法手动删除缓存。但是这个缓存并没有必要,缓存是根据NSFetchRequest对象来匹配的,如果当前获取的数据和之前缓存的相匹配则直接拿来用,但是在获取数据时每次获取的数据都可能不同,缓存不能被命中则很难派上用场,而且缓存还占用着内存资源。在fetchedResultController初始化完成后,调用performFetch:方法来同步获取持久化存储区数据,调用此方法后FRC保存数据的属性才会有值。获取到数据后,调用tableView的reloadData方法,会回调tableView的代理方法,可以在tableView的代理方法中获取到FRC的数据。调用performFetch:方法第一次获取到数据并不会回调FRC代理方法。代理方法fetchedResultController中包含UITableView执行过程中需要的相关数据,可以通过fetchedResultController的sections属性,获取一个遵守协议(NSFetchedResultsSectionInfo)的对象数组,数组中的对象就代表一个section。在这个协议中有如下定义,可以看出这些属性和UITableView的执行流程是紧密相关的。
//通过fetchedResultController中的sections(sections对应的是所有实体的所有对象)属性获取所有的section对象(一个section对应一个实体的所有对象,section也是一个数组)
就像上面cellForRowAtIndexPath:方法中使用的一样,FRC提供了两个方法轻松转换indexPath和NSManagedObject的对象,在实际开发中这两个方法非常实用,这也是FRC和UITableView、UICollectionView深度融合的表现。
-(NSArray *)contactEntity{if (_contactEntity ==nil)
{_contactEntity = [NSArrayarray];}return_contactEntity;}- (void)viewDidLoad {[superviewDidLoad];// 查询数据[self.fetchController performFetch:nil];//执行查询获取请求self.contactEntity = self.fetchController.fetchedObjects;}//查询控制器请求代理方法//可以通过上面的NSFetchedResultsSectionInfo协议中的sections取查询后的数据,也可以通过NSFetchedResultsController的fetchedObjects属性取数据(fetchedObjects存的是所有实体对象)-(void)controllerDidChangeContent:(NSFetchedResultsController*)controller{//重新获取数据self.contactEntity =self.fetchController.fetchedObjects;//self.contactEntity=controller.fetchedObjects;//刷新[self.tableViewreloadData];}+++++++++++++++++++++++++++++===================
//对数组的排序NSSortDescriptor *heigtSort = [NSSortDescriptorsort DescriptorWithKey:@"height" ascending:NO];// NSArray *resultArr=[arr sortedArrayUsingDescriptor:@[heigtSort]];==============================================CoreData中的多线程问题
1.一种比较好的iOS模式就是使用一个NSPersistentStoreCoordinator,以及两个独立的Contexts,一个context负责主线程与UI协作,一个context在后台负责耗时的处理,用Notifications的方式通知主线程的NSManagedObjectContext进行mergeChangesFromContextDidSaveNotification操作
2.后台线程做读写更新,而主线程只读
3.CoreData中的NSManagedObjectContext在多线程中不安全,如果想要多线程访问CoreData的话,最好的方法是一个线程一个NSManagedObjectContext,每个NSManagedObjectContext对象实例都可以使用同一个NSPersistentStoreCoordinator实例,这个实例可以很安全的顺序访_问永久存储,这是因为NSManagedObjectContext会在便用NSPersistentStoreCoordinator前上锁。ios5.0为NSManagedObjectContext提供了initWithConcurrentcyType方法,其中的一个NSPrivateQueueConcurrencyType,会自动的创建一个新线程来存放NSManagedObjectContext而且它还会自动创建NSPersistentStoreCoordinator,

CoreData与多线程
为了在查询数据的时候不让界面停滞,使用多线程是不可避免,一般我们会用thread,串行线程或者并发线程。
coredata与多线程交互的时候,每个线程都必须拥有一个manager context对象,一般有两种方式:
1.每一个线程使用私有的manager context,共享一个 persistent store coordinator
2.每个线程使用私有的manager context和私有的persistent store coordinator
对于这两种方式,我们比较推荐使用第一钟方式,因为使用第二种方式的会消耗我们更多的内存,所以推荐使用第一种。CoreData里面还带有一个通知NSManagedObjectContextDidSaveNotification,主要监听NSManagedObjectContext的数据是否改变,并合并数据改变到相应context=============================
另一种解释:

CoreData 多线程下NSManagedObjectContext的使用,nsmanagedobject

介绍NSManagedObjectContext在多线程下的三种设计,下面我将一一介绍:
1. persistentStoreCoordinator<-mainContext<-privateContext
这种设计就是我之前在项目中使用的,也是阻塞UI线程最严重的一种设计。它总共有两个Context,一个是UI线程中使用的mainContext,一个是子线程中使用的privateContext,他们的关系是privateContext.parentContext = mainContext,而mainContext是与Disk连接的Context,所以这种设计下,每当子线程privateContext进行save操作以后,它会将数据库所有变动Push
up到其父Context,也就是mainContext中去,注意:这时子线程的save操作并没有任何关于Disk IO的操作。而后mainContext在UI线程又要执行一次save操作才能真正将数据变动写进数据库中,这里的save操作就与Disk IO有关了,而且又是在主线程,所以说这种设计是最阻碍UI线程的。2. persistentStoreCoordinator<-backgroundContext<-mainContext<-privateContext
这种设计是第一种的改进设计,也是上述的老外博主推荐的一种设计方式。它总共有三个Context,一是连接persistentStoreCoordinator也是最底层的backgroundContext,二是UI线程的mainContext,三是子线程的privateContext,后两个Context在1中已经介绍过了,这里就不再具体介绍,他们的关系是privateContext.parentContext = mainContext, mainContext.parentContext = backgroundContext。下面说说它的具体工作流程。
在应用中,如果我们有API操作,首先我们会起一个子线程进行API请求,在得到Response后要进行数据库操作,这是我们要创建一个privateContext进行数据的增删改查,然后call privateContext的save方法进行存储,这里的save操作只是将所有数据变动Push up到它的父Context中也就是mainContext中,然后mainContext继续call save方法,将数据变动Push up到它的父Context中也就是backgroundContext,最后调用backgroundContext的save方法真正将数据变动存储到Disk数据库中,在这个过程中,前两个save操作相对耗时较少,真正耗时的操作是最后backgroundContext的save操作,因为只有它有Disk
IO的操作。3. persistentStoreCoordinator<-mainContext persistentStoreCoordinator<-privateContext
第三种设计是最直观的一种设计,无论是mainContext还是privateContext都是连接persistentStoreCoordinator的。这种设计的工作流程是:
首先在ViewController中要添加一个名为NSManagedObjectContextDidSaveNotification的通知 ,
然后子线程中创建privateContext,进行数据增删改查操作,直接save到本地数据库,这时在ViewController中会回调之前注册的NSManagedObjectContextDidSaveNotification的回调方法,在该方法中调用mainContext的mergeChangesFromContextDidSaveNotification:notification方法,将所有的数据变动merge到mainContext中,这样就保持了两个Context中的数据同步。由于大部分的操作都是privateContext在子线程中操作的,所以这种设计是UI线程耗时最少的一种设计,但是它的代价是需要多写mergeChanges的方法。(注:前两种parent,child的Context,一旦childContext调用save方法,其parentContext不用任何merge操作,CoreData自动将数据merge到parentContext当中)总结:
第一种设计是失败的设计,完全可以不考虑,第二种设计比较复杂繁琐,但是它是最方便而且UI线程的阻塞时间也是相对较少的一种。第三种设计是最少阻塞UI的一种,但是这种设计操作比较繁琐,应用场合是数据量比较大的应用,一般会应用在企业应用中,所以如果你不是企业级的应用或者不是数据量很大的应用,我还是推荐第二种设计。
通常主线程context使用NSMainQueueConcurrencyType,其他线程childContext使用NSPrivateQueueConcurrencyType. child和parent的特点是要用Block进行操作,performBlock,或者performBlockAndWait,保证线程安全。这两个函数的区别是performBlock不会阻塞运行的线程,相当于异步操作,performBlockAndWait会阻塞运行线程,相当于同步操作。
-(
void
)main{
02.
self.privateContext
= [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
03.
[self.privateContext
setPersistentStoreCoordinator:self.mainContext.persistentStoreCoordinator];
04.
05.
[[NSNotificationCenter
defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification *note) {
06.
if
(note.object
== self.privateContext) {
07.
dispatch_async(dispatch_get_main_queue(),
^{//回到主线程更新
08.
[self.mainContext
performBlock:^{
09.
[self.mainContext
mergeChangesFromContextDidSaveNotification:note];
10.
}];
11.
});
12.
}
13.
}];
14.
15.
//执行耗时的操作
16.
17.
//执行完毕
18.
[self.privateContext
performBlock:^{
19.
NSError
* error = nil;
20.
if
([self.privateContext
hasChanges]) {
21.
[self.privateContext
save:&error];//保存之后就会发送名为NSManagedObjectContextDidSaveNotification的通知;
22.
23.
}];
24.
}</
********实例二
参考:http://www.jianshu.com/p/37ab8f336f76CoreData中的NSManagedObjectContext在多线程中不安全,如果想要多线程访问CoreData的话,最好的方法是一个线程一个NSManagedObjectContext,每个NSManagedObjectContext对象实例都可以使用同一个
NSPersistentStoreCoordinator实例,这个实例可以很安全的顺序访问永久存储,这是因为 NSManagedObjectContext会在使用NSPersistentStoreCoordinator前上锁。初始化一个子线程中的管理上下文:NSManagedObjectContext *pravite = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];[pravite setParentContext:self.moc];//注册通知[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mocDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification
object:nil];[pravite performBlock:^{//自定开辟子线程NSLog(@"%@",[NSThread currentThread]);//执行操作//同步保存NSError *error = nil;if ([pravite hasChanges] && ![pravite save:&error]) {NSLog(@"%@",error);abort();//异常终止一个进程}}];子线程管理上下文在多线程中执行任务,管理数据,内容发生变化,触发通知,执行通知方法-(void)mocDidSaveNotification:(NSNotification *)notification{NSManagedObjectContext *saveContext = [notification object];//主线程中的上下文不用保存if (self.moc == saveContext) {return;}//使用一个持久化指针if (self.moc.persistentStoreCoordinator!=saveContext.persistentStoreCoordinator) {return;}//在主线程执行合并操作dispatch_async(dispatch_get_main_queue(), ^{[self.moc mergeChangesFromContextDidSaveNotification:notification];<br>
if([self.moc hasChanges])<br>
[self.moc save:nil];});}这样将子线程中的数据合并到主线程的管理上下文中.
===================
coredata中的版本迁移:(以下是转自别人的文章)
创建新版本模型文件本文中讲的几种版本迁移方案,在迁移之前都需要对原有的模型文件创建新版本。选中需要做迁移的模型文件 -> 点击菜单栏Editor -> Add Model Version -> 选择基于哪个版本的模型文件(一般都是选择目前最新的版本),新建模型文件完成。对于新版本模型文件的命名,我在创建新版本模型文件时,一般会拿当前工程版本号当做后缀,这样在模型文件版本比较多的时候,就可以很容易将模型文件版本和工程版本对应起来。创建新版本模型文件添加完成后,会发现之前的模型文件会变成一个文件夹,里面包含着多个模型文件。模型文件夹在新建的模型文件中,里面的文件结构和之前的文件结构相同。后续的修改都应该在新的模型文件上,之前的模型文件不要再动了,在修改完模型文件后,记得更新对应的模型类文件。基于新的模型文件,对Employee实体做如下修改,下面的版本迁移也以此为例。修改之前添加一个String类型的属性,设置属性名为sectionName。修改之后此时还应该选中模型文件,设置当前模型文件的版本。这里选择将最新版本设置为刚才新建的1.1.0版本,模型文件设置工作完成。Show The File Inspector -> Model Version -> Current 设置为最新版本。设置版本对模型文件的设置已经完成了,接下来系统还要知道我们想要怎样迁移数据。在迁移过程中可能会存在多种可能,苹果将这个灵活性留给了我们完成。剩下要做的就是编写迁移方案以及细节的代码。轻量级版本迁移轻量级版本迁移方案非常简单,大多数迁移工作都是由系统完成的,只需要告诉系统迁移方式即可。在持久化存储协调器(PSC)初始化对应的持久化存储(NSPersistentStore)对象时,设置options参数即可,参数是一个字典。PSC会根据传入的字典,自动推断版本迁移的过程。字典中设置的key:NSMigratePersistentStoresAutomaticallyOption设置为YES,CoreData会试着把低版本的持久化存储区迁移到最新版本的模型文件。NSInferMappingModelAutomaticallyOption设置为YES,CoreData会试着以最为合理地方式自动推断出源模型文件的实体中,某个属性到底对应于目标模型文件实体中的哪一个属性。版本迁移的设置是在创建MOC时给PSC设置的,为了使代码更直观,下面只给出发生变化部分的代码,其他MOC的初始化代码都不变。
修改实体名假设需要对已存在实体进行改名操作,需要将重命名后的实体Renaming ID,设置为之前的实体名。下面是Employee实体进行操作。修改实体名修改后再使用实体时,应该将实体名设为最新的实体名,这里也就是Employee2,而且数据库中的数据也会迁移到Employee2表中。
版本迁移方法二:Mapping Model
迁移方案(有待研究)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: