1. 线程访问限制
- NSManagedObjectContext不允许跨线程操作
- NSManagedObject不允许跨线程访问
这两处所说的线程指的NSManagedObjectContext类私有的_dispatchQueue
, 我把它叫做MOC的操作线程. MOC还有另外一个私有的_queueOwner
, 指的是创建这个MOC的线程,我把它叫做MOC的所属线程。 这两者不是一回事。
本节参见:
- https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Conceptual/CoreData/Concurrency.html#//apple_ref/doc/uid/TP40001075-CH24-SW1
- https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/index.html#//apple_ref/doc/uid/TP30001182-256616
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的成本要高的。
具体的性能数据,有兴趣的可以参考Florian大神的两篇post:
- http://floriankugler.com/2013/04/02/the-concurrent-core-data-stack/
- http://floriankugler.com/blog/2013/4/29/concurrent-core-data-stack-performance-shootout
5. 关于Core Data多线程的debug
iOS 8.0以上,在 Arguments中添加: -com.apple.CoreData.ConcurrencyDebug 1
,系统便会自动加载CoreData debug版本的库。当违反了多线程操作原则时便会强制crash。
说明:本文为原创内容,转载请注明出处