最近读了Coredata第一三方库Magical Record作者的一篇文章链接描述,对Coredata访问有了新的疑惑,performBlock
API 是如何保证线程安全的?
众所周知 , NSManagedObjectContext(简称moc) 是不允许跨线程访问的 ,用人话说就是 在哪个线程创建的moc ,就在哪个线程使用它.
苹果在iOS5.0之后 推出了一个新的 api 操作moc. 有代码长这样 :
__block BOOL savedOK = NO;
[myMOC performBlock:^{
// Do lots of things with the context.
savedOK = [myMOC save:&error];
}];
即是通过NSPrivateQueueConcurrencyType
类型创建的MOC 系统会为其分配一个私有队列,而不需要我们手动为它创建线程, 从此并发变的更容易 。
但是通过上面那篇文章, Magical Record 作者指出, 苹果的新api 使用了GCD , 而在GCD中的并发我们是通过队列来实现的,即我们只关注和操作队列,而不是直接操作线程。 当你提交一个block代码块到GCD的队列上,队列会自动分配线程去执行这个block。 但是,GCD不能保证这个队列始终在一条线程运行!! 也就是说,当你提交block到队列时,是将任务放到线程A执行,但下一刻,也许任务已经不在线程A而在线程B了!虽然此时依旧在同一个队列!这即是作者弃用contextForCurrentThread
的原因。
那么问题来了,如上文代码中,myMOC是一个NSPrivateQueueConcurrencyType
类型的上下文,创建时系统为其分配了私有队列Q,我们假设myMOC创建发生在线程A,线程A属于队列Q,但由于GCD的特性,某一刻将block任务分配到了属于队列Q的线程B ,也就是说,此时,创建于线程A的myMOC 可能会在线程B被访问 , 这就发生了跨线程访问!
这又如何解释呢? 希望对这部分了解的大虾给予解答,如果我解决了这个问题,也会在楼下贴出 。
非常感谢你的回答. 不过这似乎还有两个疑问:
1.作者那篇文章确实没有直接说出“苹果的新api使用了GCD",原文中有一句
However, now, as more of you (and the heart of MagicalRecord) is using GCD
, 是说MagicalRecord的核心是用了GCD,可我翻遍MagicalRecord的源码,似乎并没有发现使用GCD调度队列来实现异步操作,核心都是用了performBlock
和performBlockAndWait
来实现对MOC的异步操作.所以我猜测作者的意思是performBlock
会与GCD有关,不然为何 明明没有用到GCD,却因为GCD的特性而弃用之前的接口contextForCurrentThread
呢?2.抛去问题1, 苹果文档有一部分这样说:
NSPrivateQueueConcurrencyType类型的MOC在初始化时会创建它自己的队列,此队列是这个MOC内部私有的。那么是否可以理解为 :
我在线程a中创建了一个
NSPrivateQueueConcurrencyType
的MOC,它会创建一个私有队列,在线程b中去管理performBlock
下的内容.在线程a中创建context,则它是应该属于线程a的,却在队列的线程b中访问,岂不是跨线程访问了?
又与
performBlock
会保障在当前context所属的thread中执行矛盾了吗?它是如何保障的,难道是创建私有队列时令b与a是同一个线程?怎么做的呢?我看了你给的那个链接的文章,“但是通过上面那篇文章, Magical Record 作者指出, 苹果的新api 使用了GCD , 而在GCD中的并发我们是通过队列来实现的”,并没有看到你上面这句中说的”苹果的新 api 使用了 GCD" 这样的描述。
下面是引用的苹果的文档, performBlock 方法是会保障在当前 context 所属的 thread 里执行的: