通常情况下,CoreData 的增删改查操作都在主线程上执行,那么对数据库的操作就会影响到 UI 操作,这在操作的数据量比较小的时候,执行的速度很快,我们也不会察觉到对 UI 的影响,但是当数据量特别大的时候,再把 CoreData 的操作放到主线程中就会影响到 UI 的流畅性。自然而然地我们就会想到使用后台线程来处理大量的数据操作。

使用后台 managedObjectContext

CoreData 里使用后台更新数据最常用的方案是一个 persistentStoreCoordinator 持久化存储协调器对应两个 managedObjectContext 管理上下文,NSManagedObjectContext 在创建时,可以传入 ConcurrencyType 来指定 context 的并发类型。指定 NSMainQueueConcurrencyType 就是我们平时创建的运行在主队列的 context;指定成 NSPrivateQueueConcurrencyType 的话,context 就会运行在它所管理的一个私有队列中;另外还有 NSConfinementConcurrencyType 是适用于旧设备的并发类型,现在已经被废弃了,所以实际上只有两种并发类型。
下面是创建 backgroundContext 的代码:

  1. NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
  2. backgroundContext.persistentStoreCoordinator = self.persistentStoreCoordinator;

在最新的 iOS 10 中,CoreData 栈的创建被封装在了 NSPersistentContainer 类中,用它来创建 backgroundContext 更加简单:

  1. NSManagedObjectContext *backgroundContext = ((AppDelegate *)[UIApplication sharedApplication].delegate).persistentContainer.newBackgroundContext;

另外,后台 context 的操作得放在 performBlockperformBlockAndWait 方法里执行,performBlock 会异步的执行,不会阻塞当前的线程,而 performBlockAndWait 则会阻塞当前的线程直到方法返回才会继续向下执行。下面是一段后台插入数据的示例代码:

  1. [self.backgroundContext performBlock:^{
  2. for (NSUInteger i = 0; i < 100000; i++) {
  3. NSString *name = [NSString stringWithFormat:@"student-%d", arc4random_uniform(9999)];
  4. int16_t age = arc4random_uniform(10) + 10;
  5. int16_t stuId = arc4random_uniform(9999);
  6. Student *student = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:self.backgroundContext];
  7. student.studentName = name;
  8. student.studentAge = age;
  9. student.studentId = stuId;
  10. }
  11. NSError *error;
  12. [self.backgroundContext save:&error];
  13. [self.logger dealWithError:error
  14. whenFail:@"failed to insert"
  15. whenSuccess:@"insert success"];
  16. }];

后台插入数据之后,还没有完,因为数据是通过后台的 context 写入到本地的持久化数据库的,所以这时候主队列的 context 是不知道本地数据变化的,所以还需要通知到主队列的 context:“数据库的内容有变化啦,看看你有没有需要合并的”。这个过程可以通过监听一条通知来实现。这个通知就是 NSManagedObjectContextDidSaveNotification,在每次调用 NSManagedObjectContextsave:方法时都会自动发送,通知中的 userInfo 中包含了修改的数据,可以通过 NSInsertedObjectsKeyNSUpdatedObjectsKeyNSDeletedObjectsKey 这三个 key 获取到。

NSManagedObjectContextDidSaveNotification-w600

收到通知之后,只需要调用 [self.mainContext mergeChangesFromContextDidSaveNotification:note] 就可以将修改的数据合并到主线程的 context

下面是示例代码:

  1. - (void)viewDidLoad {
  2. [super viewDidLoad];
  3. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveContextSave:) name:NSManagedObjectContextDidSaveNotification object:self.backgroundContext];
  4. }
  5. - (void)doSometingInsertingInBackground {
  6. // backgroundContext ....
  7. }
  8. - (void)receiveContextSave:(NSNotification *)note {
  9. [self.context mergeChangesFromContextDidSaveNotification:note];
  10. }
  11. - (void)dealloc {
  12. [[NSNotificationCenter defaultCenter] removeObserver:self];
  13. }

注意:通知的 userInfo 里保存的 managedObjects 不可以直接在另一个线程的 context 中直接使用!也就是 managedObject 不是跨线程的,如果想要在别的线程操作,必须通过 objectId 在另一个 context 里再重新获得这个 object

  1. - (void)receiveContextSave:(NSNotification *)note {
  2. [self.context mergeChangesFromContextDidSaveNotification:note];
  3. NSSet<Student *> *managedObjects = note.userInfo[NSInsertedObjectsKey];
  4. NSManagedObjectID *studentId = managedObjects.allObjects[0].objectID;
  5. [self.context performBlock:^{
  6. // 这是错的
  7. // Student *wrongStudent = managedObjects.allObjects[0];
  8. // 应该这么做
  9. Student *student = [self.context objectWithID:studentId];
  10. // modify student...
  11. }];
  12. }

CoreData 从入门到精通(四)并发操作的更多相关文章

  1. CoreData 从入门到精通(五)CoreData 和 TableView 结合

    我们知道 CoreData 里存储的是具有相同结构的一系列数据的集合,TableView 正好是用列表来展示一系列具有相同结构的数据集合的.所以,要是 CoreData 和 TableView 能结合 ...

  2. CoreData 从入门到精通(三)关联表的创建

    上篇博客中讲了 CoreData 里增删改查的使用,学到这里已经可以应对简单的数据存储需求了.但是当数据模型复杂起来时,例如你的模型类中除了要存储 CoreData 里支持的数据类型外,还有一些自定义 ...

  3. MyBatis从入门到精通(四):MyBatis XML方式的基本用法之增删改

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. insert用法 1.1 简单的 ...

  4. Python基本语法[二],python入门到精通[四]

    在上一篇博客Python基本语法,python入门到精通[二]已经为大家简单介绍了一下python的基本语法,上一篇博客的基本语法只是一个预览版的,目的是让大家对python的基本语法有个大概的了解. ...

  5. CoreData 从入门到精通 (一) 数据模型 + CoreData 栈的创建

    CoreData 是 Cocoa 平台上用来管理模型层数据和数据持久化的一个框架,说简单点,就是一个数据库存储框架.CoreData 里相关的概念比较多,而且初始化也非常繁琐,所以对初学者的学习还是有 ...

  6. iOS开发-UI 从入门到精通(四)

    一.UITextField 1.UITextField是什么? (1)UITextField(输入框):是控制文本输入和显示的控件.在App中UITextField出现频率也比较高: (2)iOS系统 ...

  7. CoreData 从入门到精通(六)模型版本和数据迁移

    前面几篇文章中讲的所有内容,都是在同一个模型版本上进行操作的.但在真实开发中,基本上不会一直停留在一个版本上,因为需求是不断变化的,说不定什么时候就需要往模型里添加新的字段,添加新的模型,甚至是大规模 ...

  8. CoreData 从入门到精通(二) 数据的增删改查

    在上篇博客中,讲了数据模型和 CoreData 栈的创建,那下一步就是对数据的操作了.和数据库一样,CoreData 里的操作也无非是增删改查.下面我们将逐步讲解在 CoreData 中进行增删改查的 ...

  9. Atom编辑器入门到精通(四) Atom使用进阶

    在本节中将介绍Atom提供的更高级的使用技巧,通过这些技巧将会进一步提高你的代码编写效率 代码片段(Snippets) Snippets是一种在代码中快捷插入代码块的方式,下面是维基百科中对Snipp ...

随机推荐

  1. python黏包解决方案

    解决方案 # 我们可以借助一个模块,这个模块可以把要发送的数据长度转换成固定长度的字节.这样客户端每次接 # 收消息之前只要先接受这个固定长度字节的内容看一看接下来要接收的信息大小,那么最终接受的数据 ...

  2. CentOS 6.7操作系统安装

    如果由于是显卡驱动不兼容的话,在选择安装界面按tab键,进入命令行,然后在命令行后加上 nodmraid 关键字回车开始安装. 接下来选择hard driver   选择最后一个分区进行系统安装,然后 ...

  3. 在Android源码下编译jni所需要知道的事~

    以下只是自己的一些总结,欢迎讨论 通过NDK编译jni网上有很多例子,在这我只总结在Android源码下编译 1.android源码环境下编译so包,编出来的.so的包前面不会自动给添加lib,NDK ...

  4. FLAG_DISMISS_KEYGUARD & FLAG_SHOW_WHEN_LOCKED

    getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD): 这一个标志的意思是去掉锁屏界面,但这对安全锁(图案或者密 ...

  5. WebApi在MVC 4中一个Controll多个post方法报错处理

    http://blog.csdn.net/lqh4188/article/details/53542400(原创)

  6. vue 特定条件下绑定事件

    今天写了个小功能,看起来挺简单,写的过程中发现了些坑.1.div没有disabled的属性,所以得写成button2.disabled在data时,默认是true,使得初始化时,默认置灰按钮,无法点击 ...

  7. 编码介绍(ANSI、GBK、GB2312、UTF-8、GB18030和 UNICODE)

    转载:http://blog.jobbole.com/30526/(前面内容)和http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf ...

  8. web产品浏览器兼容性问题你有考虑到吗?

    通常,动态网页除了Server端的代码撰写Client端代码也必须下不少工夫.例如:表单提交前的数据验证.图片的轮播.菜单的收合等等. 因此,对于Client端是否能正常执行指令码也必须适当的考察,然 ...

  9. git + grunt 环境配置

        1⃣️ssh key生成步骤 一.设置Git的user.name和user.email: $ git config --global user.name "xiongzuyan&qu ...

  10. php方法-------将汉字转为拼音或者提取汉字首字母

    将汉字转为全拼,提取汉字首字母 <?php /** * 基于PHP语言的汉语转拼音的类 * 兼容 UTF8.GBK.GB2312 编码,无须特殊处理 * 对中文默认返回拼音首字母缩写,其它字符不 ...