最近读了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 裡執行的: