最近读了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被访问 , 这就发生了跨线程访问!
这又如何解释呢? 希望对这部分了解的大虾给予解答,如果我解决了这个问题,也会在楼下贴出 。
ご回答いただきありがとうございます。しかし、さらに 2 つの質問があるようです:
1. 著者の記事には直接「Apple の新しい API は GCD を使用している」とは書かれていませんでしたが、元の記事には、MagicalRecord のコアが GCD を使用していることを意味する一文がありますが、ソースコードを調べてみました。 MagicRecord を調べたところ、GCD スケジューリング キューを使用して非同期操作を実装するという発見はないようです。コアは
スレッド a でHowever, now, as more of you (and the heart of MagicalRecord) is using GCD
とperformBlock
を使用して MOC で非同期操作を実装するということだと思います。 GCD に関連しています。そうでない場合は、GCD が使用されていないのはなぜですか?performBlockAndWait
2. 質問 1 はさておき、Apple のドキュメントの一部には次のように書かれています。performBlock
リーリーcontextForCurrentThread
NSPrivateQueueConcurrencyType タイプの MOC は、初期化中に独自のキューを作成します。このキューは、この MOC 内でプライベートです。したがって、これは次のように理解できます:の MOC を作成しました。これにより、プライベート キューが作成され、スレッド b の
でコンテンツが管理されます。リーリー
と競合しますか?プライベートキューを作成するときに b と a が同じスレッドであることはどのように保証されますか?どうやってやるの?スレッド a で作成されたコンテキストはスレッド a に属するはずですが、キューのスレッド b でアクセスされます。これはクロススレッド アクセスではありませんか?
NSPrivateQueueConcurrencyType
performBlock
は、現在のコンテキストが属するスレッドでの実行を保証するリンク先の記事を読みましたが、「しかし、上記の記事を通じて、Magical Record の著者は、Apple の新しい API は GCD を使用しており、キューを介して GCD で同時実行性を実装していると指摘しました。」という記述は見当たりませんでした。 「Apple の新しい API は GCD を使用する」という上の文。
以下は、参照される Apple ドキュメントです。performBlock メソッドは、現在のコンテキストが属するスレッドで確実に実行されます。 リーリー