1. 线程访问限制
- NSManagedObjectContext 不允许跨线程操作
- NSManagedObject 不允许跨线程访问
这两处所说的线程指的NSManagedObjectContext类私有的_dispatchQueue
, 我把它叫做MOC的操作线程
.
MOC还有另外一个私有的_queueOwner
, 指的是创建这个MOC的线程,我把它叫做MOC的所属线程
。 这两者不是一回事。
本节参见:
Core Data Programming Guide: Concurrency
NSManagedObjectContext | Apple Developer Documentation
2. NSManagedObjectContext与多线程相关的说明
MOC有两种类型: 基于线程限制的和基于queue的。
-
基于线程限制的MOC,需要保证在MOC的所有操作需要在它的创建线程上。也就是说, 它的
操作线程
和所属线程
为同一个线程。这种类型的MOC是通过-init
方法初始化的,且要求它的parent store是PSC。
使用这种基于线程限制的MOC时,需要编程者自己保证仅在它的创建线程上操作它。 -
基于queue的MOC,为了保证它的所有操作都在其操作线程上(且其操作线程私有),只需要把操作包装在
performBlock:
或者performBlockAndWait:
中。而这两个方法本身是可以在任意线程中调用的。这种类型的MOC,其操作线程和所属线程一般不是同一个线程(NSMainQueueConcurrencyType类型的是例外)。
以上两种类型的MOC, 其所属线程
均为调用它的init方法的线程。
3. 数据同步
当操作线程为A的MOC里的数据有变化时,另一个操作线程为B的MOC如何同步这种变化呢? 两种方案:
- 通过监听NSManagedObjectContextDidSaveNotification通知,主动merge.
- 通过parent/child nested context. 当child context做save操作后,这种变化会自动同步到parent context中。
MagicalRecord是针对Core Data的二次封装,很好的处理了多线程相关的操作。另外一篇文章中简单分析了MagicalRecord的设计,感兴趣的可以阅读一下。
本节参见:
https://forums.pragprog.com/forums/252/topics/12271
http://oleb.net/blog/2014/06/core-data-concurrency-debugging/#fnref:3
4. 常见的多线程设计方案
根据Core Data大神Florin Kugler的介绍,app中常见的多线程设计的方案有一下三种:
方案1:
方案2:
方案3:
从性能上看,这三种方案中第一种方案对主线程的影响最大。因为虽然耗时的数据操作是放在Bg MOC中操作的,但最终持久化到DB中以及从DB中获取数据均需要通过main MOC。第二种方案的设计的性能介于第二种和第三种之间,但各个context之间的数据同步是通过parent context关系自动同步的,比较优雅。第三种方案的性能最优,但context之间是通过通知来进行的,相对比较复杂。 其中第二种方案的性能差于第三种方案,说明自动save的成本还是比主动merge的成本要高的。
5. 关于Core Data多线程的debug
iOS 8.0以上,在 Arguments中添加:-com.apple.CoreData.ConcurrencyDebug 1,系统便会自动加载CoreData debug版本的库。当违反了多线程操作原则时便会强制crash。
说明:本文为原创内容,转载请注明出处