zz 说说iOS的多线程Core Data

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操作有没有写入到持久层。

时间: 2024-07-28 21:25:08

zz 说说iOS的多线程Core Data的相关文章

ios开发:Core Data概述

Core Data 概述 2005年的四月份,Apple 发布了 OS X 10.4,在这个版本中 Core Data 框架发布了.Core Data本身既不是数据库也不是数据库访问框架.相反,Core Data是一个完整的数据模型解决方案.可以简单理解为对持久层的封装,使得我们可以通过可视化建立数据模型,简化数据存取.即使不懂SQL语句,也依然可以使用Core Data.因为Core Data将底层的数据库SQL语句封装成了一套API,并可通过可视化操作来建立数据库的模型和表之间的关系,它甚至

IOS开发之--Core Data的使用(进阶)

CoreData的使用(进阶) 本次目标是创建一个应用程序,可以记录每次你保存的经纬度坐标,并且可以对这些坐标(我们保存为一个Event实体)进行编辑. 建立工程 步骤 创建一个Empty Application,起名叫Locations,选择Devices为iPhone,并且使用ARC: 添加CoreLocation.framework: 添加一个Storyboard文件,并在工程属性中选择Main Storyboard为这个文件: 至此,操作步骤完成. 对工程的理解 以上步骤完成后,我们的工

IOS开发之--Core Data的使用

Core Data基础知识 官方的说法是:Core Data is a schema-driven object graph management and persistence framework. 翻译过来的意思大概是:Core Data是一个模式驱动的对象图管理和持久化框架. 好吧,上面的字面意思不是很容易理解,那么我们从以下几个方面来帮助那些有其余开发经验的程序员树立一些观念: Core Data不是一个数据库,但是他可能使用一个数据库.默认情况下,Core Data将使用SQLite,

iOS教程:Core Data数据持久性存储基础教程

其实最近更多的是在写这篇文章<iOS教程:使用持久性数据Core Data>,这篇是<iOS开发教程:Storyboard全解析-第一部分>这篇的后续,但是目前还没有完成,先放出一个持久性数据存储的教程以供参考.这其实是一篇翻译文章,英文的原文见这里.我翻译的过程中改变了一些内容以便适应我们中国人的口味,下面请看教程: 就像我一直说的,Core Data是iOS编程,乃至Mac编程中使用持久性数据存储的最佳方式,本质上来说,Core Data使用的就是SQLite,但是通过一系列特

iOS教程:如何使用Core Data – 预加载和引入数据

这是接着上一次<iOS教程:Core Data数据持久性存储基础教程>的后续教程,程序也会使用上一次制作完成的. 再上一个教程中,我们只做了一个数据模型,之后我们使用这个数据模型中的数据创建了一个表视图,我们还学习了如何测试数据模型的可行性,今天,我们来看看如何在应用启动的时候,将已经存在的数据载入或者引用到我们的程序中去. 请注意我们在上一次的教程中学习到的是直接通过操作SQLite数据库来加载数据,你当然可以一直使用这种方法,但是这个教程教授的方法更加优雅,更加合理. 在下一部分的教程中,

Core Data 使用

简单的介绍一下 ios 数据持久化  Core Data 使用 话不多说,直接上图 接下来,在工程中 command+N 新建Core Data 文件 创建成功后,接下编辑Core Data 文件 接下来,给实体添加属性 依照添加的属性来生成相应的Model User 实体打上勾下一步 工程会自动生成 User 的Model #import <Foundation/Foundation.h> #import <CoreData/CoreData.h> @interface User

iOS Core data多线程并发访问的问题

大家都知道Core data本身并不是一个并发安全的架构:不过针对多线程访问带来的问题,Apple给出了很多指导:同时很多第三方的开发者也贡献了很多解决方法.不过最近碰到的一个问题很奇怪,觉得有一定的特殊性,与大家分享一下. 这个问题似乎在7.0.1以前的版本上并不存在:不过后来我升级版本到了7.0.4.app的模型很简单,主线程在前台对数据库进行读写,而后台线程不断地做扫描(只读).为此每个线程中各创建了一个NSManagedObjectContext. 这个模型其实有点奇怪,因为普遍的模型是

正确使用Core Data多线程的3种方式

在#Pragma Conference 2015会议上,Marcus Zarra,撰写过关于Core Data和Core Animation的书,叙述了三种在多线程环境下使用Core Data的方法并且设法解决在2015年应如何使用Core Data的问题.实际上,Zarras说道,当用一个拥有十一年历史的技术比如Core Data工作时,你所面临的问题之一是有大量的信息是可用的,不过查明哪一份信息依旧精确以及哪一份不精确并不是一件简单的事. 根据Zarras所言,当我们知道我们仍旧有空余的CP

不再为Core Data多线程编程而头疼

by Saul Mora原文链接:http://www.cimgf.com/2011/05/04/core-data-and-threads-without-the-headache/ 我知道我曾经提到我要写一篇关于定制fetch requests的文章,然而,在我为Active Record Fetching project(现在已经改名为MagicalRecord)编写了一些代码之后,我觉得写一篇关于fetching–threading.的文章更好一些. 当大多数cocoa开发者提到Core