最近读了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被访问 , 这就发生了跨线程访问!
这又如何解释呢? 希望对这部分了解的大虾给予解答,如果我解决了这个问题,也会在楼下贴出 。
PHP中文网2017-04-17 18:00:30
ご回答いただきありがとうございます。しかし、さらに 2 つの質問があるようです:
1. 著者の記事には直接「Apple の新しい API は GCD を使用している」とは書かれていませんでしたが、元の記事には、MagicalRecord のコアが GCD を使用していることを意味する一文がありますが、ソースコードを調べてみました。 MagicRecord を調べたところ、GCD スケジューリング キューを使用して非同期操作を実装するという発見はないようです。コアは 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 の
でコンテンツが管理されます。
リーリー
スレッド a で作成されたコンテキストはスレッド a に属するはずですが、キューのスレッド b でアクセスされます。これはクロススレッド アクセスではありませんか?NSPrivateQueueConcurrencyType
performBlock
は、現在のコンテキストが属するスレッドでの実行を保証する
PHPz2017-04-17 18:00:30
リンク先の記事を読みましたが、「しかし、上記の記事を通じて、Magical Record の著者は、Apple の新しい API は GCD を使用しており、キューを介して GCD で同時実行性を実装していると指摘しました。」という記述は見当たりませんでした。 「Apple の新しい API は GCD を使用する」という上の文。
以下は、参照される Apple ドキュメントです。performBlock メソッドは、現在のコンテキストが属するスレッドで確実に実行されます。 リーリー