6

CoreData のものを実行する関数を書いています。すべての CoreData 操作が実行された後にのみ関数が返されるようにします。CoreData には、バックグラウンド コンテキストでオブジェクトを作成してから、親コンテキストでさらにいくつかの処理を行うことが含まれます。

+ (void) myFunction
    NSManagedObjectContext *backgroundContext = [DatabaseDelegate sharedDelegate].backgroundContext;

    [backgroundContext performBlockAndWait:^{
      MyObject *bla = create_my_object_in:backgroundContext;

      [backgroundContext obtainPermanentIDsForObjects:[[backgroundContext insertedObjects] allObjects] error:nil];
      [backgroundContext save:nil];

      [[DatabaseDelegate sharedDelegate].parent.managedObjectContext performBlockAndWait:^{
        [[DatabaseDelegate sharedDelegate].parent updateChangeCount:UIDocumentChangeDone];

        // Do some more stuff
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [queue addOperation:someOperation];
      }];
    }];
   return;
}

リターンが の後にのみ発生するようにし[queue addOperation:someOperation]ます。これはほとんどの場合に機能するようですが、この関数が返されない場合が 1 つあります。デッドロックのように見えましたが、 が原因であると思われperformBlockAndWaitます。

私の質問は次のとおりです。

(1) このデッドロックが発生する理由を誰か説明できますか?

(2) 同じ機能を実現する正しい方法は何ですか? 要件は、myFunction両方のブロックが実行された後にのみ戻ることです。

ありがとうございました!

4

2 に答える 2

13

myFunctionメインスレッドから呼び出しているとしましょう。[DatabaseDelegate sharedDelegate].parent.managedObjectContextメインスレッドでスケジュールされていると想像してみましょう。

[backgroundContext performBlockAndWait:]コンテキスト プライベート バックグラウンド キューでブロックをスケジュールしています。メインスレッドをブロックしています。

では[.parent.managedObjectContext performBlockAndWait:]、メイン スレッドでブロックをスケジュールし、プライベート キューをブロックします。

しかし、メインスレッドはすでにブロックされています。したがって、ブロックは決して実行されません。そして、performBlockAndWait:二度と戻りません。

デッドロック。

非同期にスケジュールされたブロックと完了ブロックを使用します。

于 2012-09-19T18:37:44.537 に答える
2

待つ必要はありません。バックグラウンド作業が実行され、それが完了する前にメインスレッドで作業が開始され、完了する前に「someOperation」が実行されます。これを async に置き換えることもできますが、それでも機能します。

このコードを見ると、ブロッキング バージョンを使用する理由はありません...

+ (void) myFunction {
    NSManagedObjectContext *backgroundContext = [DatabaseDelegate sharedDelegate].backgroundContext;

    [backgroundContext performBlock:^{
      // Asynchronous... but every command in this block will run before this
      // block returns...
      MyObject *bla = create_my_object_in:backgroundContext;

      [backgroundContext obtainPermanentIDsForObjects:[[backgroundContext insertedObjects] allObjects] error:nil];
      [backgroundContext save:nil];

      [[DatabaseDelegate sharedDelegate].parent.managedObjectContext performBlock:^{
        // Asynchronous, but this whole block will execute...
        [[DatabaseDelegate sharedDelegate].parent updateChangeCount:UIDocumentChangeDone];

        // Do some more stuff
        // This will not run until after the stuff above in this block runs...
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [queue addOperation:someOperation];
      }];
      // You will reach here BEFORE the code in the previous block executes, but
      // the "someOperation" is in that block, so it will not run until that
      // block is done.
    }];
    // Likewise, you will reach here before the above work is done, but everything
    // will still happen in the right order relative to each other.
   return;
}
于 2012-09-19T19:42:12.770 に答える