CoreData的使用
1.coreData简介
coreData是苹果对sqlite的封装,不用操作sqlite语句,他提供了对象关系映射功能,能将oc对象转化成数据,保存在sqlite中,也能将保存的数据还原成oc对象;
coredata有两种队列:私有队列,主队列
coreData中的主要包括这几个部分:管理对象上下文,数据持久化协调器,模型文件(包含实体,实体对应的是实体类),
2.coreData的使用
// 1.创建模型文件 [相当于一个数据库里的所有表]
// 2.添加实体(可以添加多个实体) ,添加相应实体的属性[添加一个实体相当于数据库中添加了一张表]
Undefined: 默认值,参与编译会报错
Integer 16: 整数,表示范围 -32768 ~ 32767
Integer 32: 整数,表示范围 -2147483648 ~ 2147483647
Integer 64: 整数,表示范围 –9223372036854775808 ~ 9223372036854775807
Float: 小数,通过MAXFLOAT宏定义来看,最大值用科学计数法表示是 0x1.fffffep+127f
Double: 小数,小数位比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];//使用这个方法,如果 bundles为nil会把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控制器实例,并绑定MOC
NSError *error = nil;
NSFetchedResultsController *fetchedResultController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context
sectionNameKeyPath:@"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的执行流程是紧密相关的。
@protocol NSFetchedResultsSectionInfo
/* Name of the section */
@property (nonatomic, readonly) NSString *name;
/* Title of the section (used when displaying the index) */
@property (nullable, nonatomic, readonly) NSString *indexTitle;
/* Number of objects in section */
@property (nonatomic, readonly) NSUInteger numberOfObjects;
/* Returns the array of objects in the section. */
@property (nullable, nonatomic, readonly) NSArray *objects;
@end
// NSFetchedResultsSectionInfo
//通过fetchedResultController中的sections(sections对应的是所有实体的所有对象)属性获取所有的section对象(一个section对应一个实体的所有对象,section也是一个数组) |
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return fetchedResultController.sections.count;
}
// 通过当前section的下标从sections数组中取出对应的section对象,并从section对象中获取所有对象count
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return fetchedResultController.sections[section].numberOfObjects;
}
// FRC根据indexPath获取托管对象,并给cell赋值
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Employee *emp = [fetchedResultController objectAtIndexPath:indexPath];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@ "identifier" forIndexPath:indexPath];
cell.textLabel.text = emp.name;
return cell;
}
// 创建FRC对象时,通过sectionNameKeyPath:传递进去的section title的属性名,在这里获取对应的属性值
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return fetchedResultController.sections[section].indexTitle;
}
// 是否可以编辑
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
YES;
}
// 这里是简单模拟UI删除cell后,本地持久化区数据和UI同步的操作。在调用下面MOC保存上下文方法后,FRC会回调代理方法并更新UI
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// 删除托管对象
Employee *emp = [fetchedResultController objectAtIndexPath:indexPath];
[context deleteObject:emp];
// 保存上下文环境,并做错误处理
NSError *error = nil;
if (![context save:&error]) {
NSLog(@ "tableView delete cell error : %@" , error);
}
}
}
|
就像上面cellForRowAtIndexPath:方法中使用的一样,FRC提供了两个方法轻松转换indexPath和NSManagedObject的对象,在实际开发中这两个方法非常实用,这也是FRC和UITableView、UICollectionView深度融合的表现。
1
2
|
- (id)objectAtIndexPath:(NSIndexPath *)indexPath;
- (nullable NSIndexPath *)indexPathForObject:(id)object;
|
// Cell数据源发生改变会回调此方法,例如添加新的托管对象等
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(nullable NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(nullable NSIndexPath *)newIndexPath {
switch
(type) {
case
NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break
;
case
NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break
;
case
NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break
;
case
NSFetchedResultsChangeUpdate: {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
Employee *emp = [fetchedResultController objectAtIndexPath:indexPath];
cell.textLabel.text = emp.name;
}
break
;
}
}
// Section数据源发生改变回调此方法,例如修改section title等。
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch
(type) {
case
NSFetchedResultsChangeInsert:
[tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
break
;
case
NSFetchedResultsChangeDelete:
[tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
break
;
default
:
break
;
}
}
// 本地数据源发生改变,将要开始回调FRC代理方法。
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[tableView beginUpdates];
}
// 本地数据源发生改变,FRC代理方法回调完成。
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[tableView endUpdates];
}
// 返回section的title,可以在这里对title做进一步处理。这里修改title后,对应section的indexTitle属性会被更新。
- (nullable NSString *)controller:(NSFetchedResultsController *)controller sectionIndexTitleForSectionName:(NSString *)sectionName {
return
[NSString stringWithFormat:@
"sectionName %@"
, sectionName];
}
-(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,
通常主线程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/37ab8f336f76
CoreData中的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的初始化代码都不变。
1
2
3
4
5
|
// 设置版本迁移方案
NSDictionary *options = @{NSMigratePersistentStoresAutomaticallyOption : @YES,
NSInferMappingModelAutomaticallyOption : @YES};
// 创建持久化存储协调器,并将迁移方案的字典当做参数传入
[coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:dataPath] options:options error:nil];
|
修改实体名
假设需要对已存在实体进行改名操作,需要将重命名后的实体Renaming ID,设置为之前的实体名。下面是Employee实体进行操作。
修改实体名
修改后再使用实体时,应该将实体名设为最新的实体名,这里也就是Employee2,而且数据库中的数据也会迁移到Employee2表中。
1
2
3
4
5
|
Employee2 *emp = [NSEntityDescription insertNewObjectForEntityForName:@
"Employee2"
inManagedObjectContext:context];
emp.name = @
"lxz"
;
emp.brithday = [NSDate date];
emp.height = @1.9;
[context save:nil];
|
版本迁移方法二:Mapping Model 迁移方案(有待研究)