Core Data是iOS中很重要的一个部分,可以理解为基于SQLite(当然也可以是其他的Storage,如In-memory,只是SQLite比较常见)的一个ORM实现,所以有关系数据库的特性,又不用写SQL。顺便吐一下槽,官方说法是使用Core Data能减少50%-70%的代码量,但相信用过的人应该都心里明白,Core Data使用起来还是比较麻烦的,这也是为什么有不少的第三方类库来代替/二次包装Core Data。

稍微复杂的应用就有可能出现同时处理多份数据的情况,这就需要用到多线程Core Data。在 iOS 5之前,官方推荐的是使用「Thread Confinement」,就是每个线程使用独立的MOC(managed object context),然后共享一个PSC(persistent store coordinator)。同时在线程之间传递数据时,要传递objectID,而不是object,因为前者是线程安全的,后者不是。

如果A线程里,对PSC执行了CUD(create, update, delete)操作,其他线程如何感知呢?这就需要通过监听事件来实现。比如在线程A中监听「NSManagedObjectContextDidSaveNotification」事件,如果线程B中执行了CUD操作,线程A就能感知到,并触发响应的action,虽然可以通过noti userinfo来获取managed objects,但因为它们是关联到另一个MOC,所以无法直接操作它们,解决方法就是调用「mergeChangesFromContextDidSaveNotification:」方法。

用一张图来形容的话,大体就是这样:

- (void)_setupCoreDataStack
{
// setup managed object model
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Database" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; // setup persistent store coordinator
NSURL *storeURL = [NSURL fileURLWithPath:[[NSString cachesPath] stringByAppendingPathComponent:@"Database.db"]]; NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:_managedObjectModel]; if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// handle error
} // create MOC
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:_persistentStoreCoordinator]; // subscribe to change notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_mocDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:nil];
}

再来看看Notification Handler,主要作用就是合并新的变化。

- (void)_mocDidSaveNotification:(NSNotification *)notification
{
NSManagedObjectContext *savedContext = [notification object]; // ignore change notifications for the main MOC
if (_managedObjectContext == savedContext) {
return;
} dispatch_sync(dispatch_get_main_queue(), ^{
[_managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
});
}

这种方式实现起来和维护起来都有点麻烦,所以iOS 5中就出现了更加方便和灵活的实现,也就是「Nested MOC」。

[[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

可以看到在初始化时可以选择ConcurrencyType,可选的有3个:

NSConfinementConcurrencyType

这个是默认项,每个线程一个独立的Context,主要是为了兼容之前的设计。

NSPrivateQueueConcurrencyType

创建一个private queue(使用GCD),这样就不会阻塞主线程。

NSMainQueueConcurrencyType

创建一个main queue,使用主线程,会阻塞。

还有一个重要的变化是MOC可以指定parent。有了parent后,CUD操作会冒泡到parent。一个parent可以有多个child。parent还可以有parent。

因为UI相关的数据必须在主线程获取,同时又要避免数据库的I/O操作阻塞主线程,所以就有了下面这个模型:

我对这种实现方式的一个困惑是:child无法得知parent的变化,也就是说,如果NSFetchedResultsController绑定了Main MOC,当Background Write MOC save时,NSFetchedResultsController为何能知晓?求指点。

这种方式比「Thread Confinement」稍微简单了点,也更明了。不过个人还是推荐使用MagicalRecord,因为实现起来更加简单,等有空再写一篇。

写了一个使用了这个模型的demo,配合TableView和NSFetchedResultsController,有兴趣的可以看下:https://github.com/limboy/coredata-with-tableview

2013/06/17更新

之前的困惑已消除,NSFetchedResultsController跟PSC无关,只要绑定的MOC有了save动作,NSFetchedResultsController就会收到通知,无论这个save操作有没有写入到持久层。

参考

coreData 深入理解4 --总结 (线程安全与同步--iOS5 前后对比)的更多相关文章

  1. 深入理解Java之线程池

    原作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本文归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则 ...

  2. 深入理解JVM(7)——线程安全和锁优化

    Java中的线程安全 按照线程安全的“安全程度”由强至弱来排序,可以将Java语中各种操作共享的数据分为以下5类:不可变. 绝对线程安全. 相对线程安全. 线程兼容和线程对立. 1.不可变 不变的对象 ...

  3. 深入理解Java之线程池(爱奇艺面试)

    爱奇艺的面试官问 (1) 线程池是如何关闭的 (2) 如何确定线程池的数量 一.线程池销毁,停止线程池 ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown() ...

  4. [转]深入理解Java之线程池

    原文链接 原文出处: 海 子 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这 ...

  5. java高并发系列 - 第5天:深入理解进程和线程

    进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.程序是指令.数据及其组织形式的描述,进程是程序的实体. 进程具有的 ...

  6. ios专题 -线程互斥与同步

    [原创]http://www.cnblogs.com/luoguoqiang1985 今天遇见了这问题,决定要需要讨论下. 线程同步的方法: @synchronized 官方文档解释:The @syn ...

  7. vc++高级班之多线程篇[6]---线程间的同步机制①

    ①.线程同步的必要性:   int g_Num = 0; UINT __cdecl ThreadProc(LPVOID lpParameter) {  for (int idx = 0; idx &l ...

  8. Java多线程——线程之间的同步

    Java多线程——线程之间的同步 摘要:本文主要学习多线程之间是如何同步的,如何使用volatile关键字,如何使用synchronized修饰的同步代码块和同步方法解决线程安全问题. 部分内容来自以 ...

  9. 线程池+同步io和异步io(浅谈)

    线程池+同步io和异步io(浅谈) 来自于知乎大佬的一个评论 我们的系统代码从同步方式+线程池改成异步化之后压测发现性能提高了一倍,不再有大量的空闲线程,但是CPU的消耗太大,几乎打满,后来改成协程化 ...

随机推荐

  1. GUI 快捷键的实现思路

      思路: 前提快捷键操作不可重复,即一个快捷键对应一个控件的动作 一个窗体保持一份快捷键的map映射 在相应的消息中获取快捷键列表如键盘消息 在控件类对象中定义一个默认的响应行为,比如Button按 ...

  2. [ucgui] 对话框5——鼠标位置和移动窗口

    >_<" 这节主要是获取鼠标的位置和把窗口设置为可以移动.其中设置窗口可以移动用FRAMEWIN_SetMoveable(hFrameWin, 1)就行了.而获得鼠标位置则是利用 ...

  3. Javascript日期与C# DateTime 转换

    DateTime的日期到了客户端为:"/Date(1346818058450+0800)/"; 转吧: var renderTime = function (dateTime) { ...

  4. JAVA学习Swing章节JPanel和JScrollPane面板的简单学习

    package com.swing; import java.awt.Container; import java.awt.GridLayout; import javax.swing.JButton ...

  5. JSTL自定义标签

    这节我们总结一下JSTL自定义标签相关内容. 1. 自定义标签简介 自定义标签主要用于移除JSP页面中的Java代码.Jsp页面主要是用来显示给前台的,如果里面有过多的java代码的话,会显得很乱,但 ...

  6. wicket基本控件使用笔记

    Label       new Label(“message”,”message content”); MutLineLabel         new MutlineLabel(“message”, ...

  7. GEF - 制作一个简单图形化编辑框架笔记1

    在首先来看看GEF是什么,GEF的全称是Graphical Editing Framework,图形化框架,可以利用此框架做图形化编.他的基本原理是采用MVC开发模式. 以下是一些例子 GEF里面包含 ...

  8. JS_Ajax基础

    一:Ajax ajax 的全称是Asynchronous(异步) JavaScript and XML 在不刷新页面的情况下从服务器获取,提交数据的一种数据交互方式; 二:Ajax使用步骤概括 //1 ...

  9. 删除配置文件解决OS X各种WiFi无法连接的顽固问题,解决MAC无法连接wif的情况 Preferences

    删除配置文件解决OS X各种WiFi无法连接的顽固问题 删除配置文件解决OS X各种WiFi无法连接的顽固问题1 记住现在wifi的密码并将wifi关闭2 前往文件夹/Library/Preferen ...

  10. Android布局优化之过度绘制

    如果一个布局十分复杂,那么就需要来排查是否出现了过度绘制,如果出现了,那么很可能会造成刷新率下降,造成卡顿的现象.那么什么是过度绘制呢?过度绘制就是在同一个区域中叠加了多个控件.这就像小时候我们画画, ...