16

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];
             });

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

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

4

4 に答える 4

29

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

Mike Ash は、GCD バリア ブロックに関する優れたブログ投稿を行ってい ます。

幸運を

T

于 2012-05-30T00:25:28.600 に答える
3

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 キューは非常に小さいため、大量のキューを問題なく作成できます。それらを使い終わったら、それらを解放するだけです。

于 2012-05-30T00:40:13.987 に答える
0

あなたが解決しようとしているように見えるものはdispatch_barrier_async、最善の解決策ではないかもしれません。Concurrency Programming GuideのMigrating Away From Threadsセクションを見てください。独自のシリアル キューを使用dispatch_syncするだけで、同期の問題が解決する場合があります。または、NSOperation と NSOperationQueue を使用できます。GCD とは異なり、NSOperation を使用すると、依存関係を簡単に管理できます (GCD を使用して行うこともできますが、すぐに見苦しくなります)。

于 2012-06-18T07:16:59.223 に答える