web-dev-qa-db-ja.com

iOSでGCDdispatch_barrier_asyncをどのように使用する必要がありますか(他のブロックの前ではなく前に実行されるようです)

IOS5で次のコードを同期しようとしています:

  1. オブジェクトには、画像へのURLなどのデータを取得するHTTPリクエストを作成するメソッドがあります
  2. データが到着すると、テキストデータを使用してCoreDataモデルにデータが入力されます
  3. 同時に、イメージをダウンロードするために2番目のスレッドが非同期でディスパッチされます。このスレッドは、画像がすでにキャッシュされてCoreDataモデルで使用可能になると、KVOを介してviewControllerに通知します。
  4. イメージのダウンロードには時間がかかるため、イメージ以外のすべての属性を持つCoreDataオブジェクトを呼び出し元にすぐに返します。
  5. また、2番目のスレッドのダウンロードが完了すると、CoreDataモデルを保存できます。

これは(簡略化された)コードです:

- (void)insideSomeMethod
{
    [SomeHTTPRequest withCompletionHandler:
     ^(id retrievedData) 
     {
         if(!retrievedData)
         {
             handler(nil);
         }

         // Populate CoreData model with retrieved Data...

         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
             NSURL* userImageURL = [NSURL URLWithString:[retrievedData valueForKey:@"imageURL"]];
             aCoreDataNSManagedObject.profileImage = [NSData dataWithContentsOfURL:userImageURL];
         });

         handler(aCoreDataNSManagedObject);
         [self shouldCommitChangesToModel];
     }];
}

- (void)shouldCommitChangesToModel
{
    dispatch_barrier_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSError *error = nil;
        if(![managedObjectContext save:&error]) 
        {
            //  Handle error
        }  
    });
}

しかし、何が起こっているのかというと、バリアベースの保存ブロックは常に画像読み込みブロックの前に実行されます。あれは、

dispatch_barrier_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            NSError *error = nil;
            if(![managedObjectContext save:&error]) 
            {
                //  Handle error
            }  
        });

前に実行:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                 NSURL* userImageURL = [NSURL URLWithString:[retrievedData valueForKey:@"imageURL"]];
                 aCoreDataNSManagedObject.profileImage = [NSData dataWithContentsOfURL:userImageURL];
             });

したがって、明らかに、バリアの前に画像読み込みブロックを実際にディスパッチしているわけではありません。そうしないと、バリアは画像読み込みブロックが完了するまで待機してから実行されます(これは私の意図でした)。

私は何が間違っているのですか?画像読み込みブロックがバリアブロックの前にキューに入れられていることを確認するにはどうすればよいですか?

15

一見したところ、問題は、グローバル同時キューにバリアブロックをディスパッチしていることである可能性があります。バリアブロックは、独自のカスタム並行キューでのみ使用できます。 dispatch_barrier_asyncのGCDドキュメントによると、ブロックをグローバルキューにディスパッチすると、通常のdispatch_async呼び出しのように動作します。

Mike Ashは、GCDバリアブロックに関する優れたブログ投稿をしています: http://www.mikeash.com/pyblog/friday-qa-2011-10-14-whats-new-in-gcd.html

幸運を

T

28
timthetoolman

ADCドキュメントのようにグローバルキューにディスパッチするのではなく、独自のキューを作成する必要があります

指定するキューは、dispatch_queue_create関数を使用して自分で作成する並行キューである必要があります。この関数に渡すキューがシリアルキューまたはグローバル同時キューの1つである場合、この関数はdispatch_async関数のように動作します。

から https://developer.Apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//Apple_ref/c/func/dispatch_barrier_async

独自のGCDキューを大量に作成できます。 gcdキューは非常に小さく、問題なく大量に作成できます。それらを使い終わったら、それらを解放する必要があります。

3
Colin Wheeler

パーティーに少し遅れましたが、次回はdispatch_groupsを活用してみてください。 http://www.raywenderlich.com/63338/grand-central-dispatch-in-depth-part-2

0
Stephen Paul

あなたが解決しようとしているように見えるものについては、dispatch_barrier_asyncは最善の解決策ではないかもしれません。並行性プログラミングガイドの スレッドからの移行 セクションをご覧ください。独自のシリアルキューでdispatch_syncを使用するだけで、同期の問題が解決する場合があります。または、NSOperationとNSOperationQueueを使用することもできます。 GCDとは異なり、NSOperationを使用すると、依存関係を簡単に管理できます(GCDを使用して管理できますが、高速になる可能性があります)。

0
quellish