iOS CoreData的使用以及coreData中的多线程问题(一)

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 部分.
file-list
file-list

这里, 实际上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对象。

QQ截图20160729183339.png

fetched Property

获取这个对象后,系统会默认将这个对象缓存到一个字典中,缓存之后也可以通过fetchedProperty字典获取fetchedProperty对象。

Fetch Requests

在模型文件中Entities下面有一个Fetch Requests,这个也是配置请求对象的。但是这个使用起来更加直观,可以很容易的完成一些简单的请求配置。相对于上面讲到的Fetched Properties,这个还是更方便使用一些。

1469788494825461.png

Fetch Requests

上面是对Employee实体的height属性配置的Fetch Request,这里配置的height要小于2米。配置之后可以通过NSManagedObjectModel类的fetchRequestTemplateForName:方法获取这个请求对象,参数是这个请求配置的名称,也就是EmployeeFR。






//    4.生成管理对象上下文 并关联模型文件生成数据库

    /*

     * 关联的时候,如果本地没有数据库文件,Coreadata自己会创建

     */

    // 创建上下文

    NSManagedObjectContext *context = [[NSManagedObjectContext allocinit];

    // 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 allocinitWithManagedObjectModel:model];

    

    // 告诉Coredata数据库的名字和路径

    NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectoryNSUserDomainMaskYESlastObject];

    

    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

{

    if (_fetchController ==nil) {

        //查询请求

        NSFetchRequest *fetchrequest = [[NSFetchRequestalloc]init];

        

        //1.获取实体描述

        fetchrequest.entity = [NSEntityDescriptionentityForName:@"XMPPUserCoreDataStorageObject"inManagedObjectContext:[XMPPRosterCoreDataStoragesharedInstance].mainThreadManagedObjectContext];

        //获取实体描述

<code class="hljs vhdl has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">    NSEntityDescription *<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">entity</span> = [NSEntityDescription entityForName:TableName inManagedObjectContext:<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">context</span>];</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"></ul>

        //2.谓词

        NSPredicate *pre = [NSPredicatepredicateWithFormat:@"subscription = %@",@"both"];

        [fetchrequest setPredicate:pre];

        

        //3.排序

        NSSortDescriptor *sort = [NSSortDescriptorsortDescriptorWithKey:@"jidStr"ascending:YES];

        fetchrequest.sortDescriptors = @[sort];

        

        

        //创建对象查询控制器对象

        _fetchController = [[NSFetchedResultsControlleralloc]initWithFetchRequest:fetchrequestmanagedObjectContext:[XMPPRosterCoreDataStoragesharedInstance].mainThreadManagedObjectContextsectionNameKeyPath:nilcacheName:@"contacts"];

        

        //代理

        _fetchController.delegate =self;

        

    }

    return_fetchController;

}


-(NSArray *)contactEntity

{

    if (_contactEntity ==nil) {

        _contactEntity = [NSArrayarray];

    }

    return_contactEntity;

}


- (void)viewDidLoad {

    [superviewDidLoad];

    

    

 // 查询数据

   [self.fetchControllerperformFetch:nil];//执行查询获取请求

    self.contactEntityself.fetchController.fetchedObjects;

}



//查询控制器请求代理方法

-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller

{

    //重新获取数据

    self.contactEntity =self.fetchController.fetchedObjects;

    

    //刷新

    [self.tableViewreloadData];

}


+++++++++++++++++++++++++++++


===================

//对数组的排序

    NSSortDescriptor *heigtSort = [NSSortDescriptorsortDescriptorWithKey:@"height"ascending:NO];

//    NSArray *resultArr=[arr sortedArrayUsingDescriptor:@[height]];






==============================================

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


在Google的时候,我发现了这样两篇老外的博客(,)。前者是介绍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];
22.
23. }];
24. }</


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值