iOS 6の完了ブロックのためのdispatch_get_current_queue()の代替手段?


101

ブロックと完了ブロックを受け入れるメソッドがあります。最初のブロックはバックグラウンドで実行し、完了ブロックはメソッドが呼び出されたキューで実行する必要があります。

後者の場合は常に使用dispatch_get_current_queue()していましたが、iOS 6以降では非推奨のようです。代わりに何を使用すればよいですか?


dispatch_get_current_queue()iOS 6で非推奨となったのはなぜですか?ドキュメントは、それについて何も言わない
jere

3
コンパイラはそれについて文句を言います。それを試してみてください。
cfischer

4
@jereヘッダーファイルを確認します。価格が低くなっています
WDUK

ベストプラクティスについての議論の他に、質問に答える[NSOperationQueue currentQueue]が表示されます。その使用についての警告についてはわかりません。
マット

見つかった警告------ [NSOperationQueue currentQueue]はdispatch_get_current_queue()と同じではありません-----ヌルを返すことがあります---- dispatch_async(dispatch_get_global_queue(0、0)、^ {NSLog(@ "q(0、 0)は%@ "、dispatch_get_current_queue()); NSLog(@" cq(0,0)is%@ "、[NSOperationQueue currentQueue]);}); ----- q(0,0)is <OS_dispatch_queue_root:com.apple.root.default-qos [0x100195140]> cq(0,0)is(null)----- depricated or not dispatch_get_current_queue()appearすべての状況で現在のキューを報告するために私が見る唯一の解決策になる
ゴジラ

回答:


64

「発信者がいたキューで実行する」というパターンは魅力的ですが、最終的には素晴らしいアイデアではありません。そのキューは、優先度の低いキュー、メインキュー、または奇妙なプロパティを持つその他のキューである可能性があります。

これに対する私のお気に入りのアプローチは、「完了ブロックは、x、y、zのプロパティを持つ実装定義のキューで実行される」と言い、呼び出し側がそれ以上の制御を必要とする場合、ブロックを特定のキューにディスパッチさせます。指定するプロパティの典型的なセットは、「シリアル、非再入可能、および他のアプリケーションから見えるキューに関して非同期」のようなものです。

**編集**

Catfish_Manが以下のコメントに例を示しました。私は彼の答えに追加しています。

- (void) aMethodWithCompletionBlock:(dispatch_block_t)completionHandler     
{ 
    dispatch_async(self.workQueue, ^{ 
        [self doSomeWork]; 
        dispatch_async(self.callbackQueue, completionHandler); 
    } 
}

7
完全に同意。Appleは常にこれに従っていることがわかります。メインキューで何かをしたいときはいつでも、メインキューにディスパッチする必要があります。これは、Appleが常に別のスレッドにいることを保証するためです。ほとんどの場合、長時間実行されているプロセスがデータのフェッチ/操作を完了するのを待っているので、完了ブロックのバックグラウンドで処理してから、メインキューのディスパッチブロックにのみUI呼び出しを挿入できます。さらに、開発者はそのパターンに慣れているので、期待に関してAppleが設定したものに従うことは常に良いことです。
Jack Lawrence、

1
偉大な答えは..しかし、私はあなたが言っているものを説明するために、少なくともいくつかのサンプルコードを期待していた
abbood

3
-(void)aMethodWithCompletionBlock:(dispatch_block_t)completionHandler {dispatch_async(self.workQueue、^ {[self doSomeWork]; dispatch_async(self.callbackQueue、completionHandler);}}
Catfish_Man

(完全に自明な例の場合)
Catfish_Man 2013年

3
dispatch_sync()とdispatch_set_target_queue()が原因で、複数のキューに同時に存在する可能性があるため(実際はかなり可能性が高いため)、一般的なケースでは不可能です。一般的なケースには、可能なサブセットがいくつかあります。
Catfish_Man 2013

27

これは、基本的に、説明するAPIにとって間違ったアプローチです。APIが実行するブロックと完了ブロックを受け入れる場合、次の事実が真である必要があります。

  1. 「実行するブロック」は内部キューで実行する必要があります。たとえば、API専用で、そのAPIの完全な制御下にあるキューなどです。これに対する唯一の例外は、ブロックがメインキューまたはグローバルコンカレントキューの1つで実行されることをAPIが明確に宣言している場合です。

  2. #1と同じ仮定が成り立つ場合を除き、完了ブロックは常にタプル(キュー、ブロック)として表現する必要あります。たとえば、完了ブロックは既知のグローバルキューで実行されます。さらに、完了ブロックは、渡されたキューで非同期にディスパッチされる必要があります。

これらは単なる文体上のポイントではなく、APIがデッドロックや、いつか最も近いツリーからハングする他のエッジケースの動作から安全である場合に完全に必要です。:-)


11
妥当に聞こえるかもしれませんが、何らかの理由でAppleが独自のAPIを採用するアプローチとは
異なり

2
確かに、完了ブロックがメインキューまたはグローバルコンカレントキューで実行されることが明らかに明らかな場合は、以前のアサーションを多少変更します。できるだけ多くのことを示すように答えを変更します。
jkh

Appleがそのアプローチを取っていないことについてコメントするには:Appleは定義ごとに常に「正しい」とは限りません。適切な議論は、科学者が確認する特定の権威よりも常に強力です。上記の答えは、適切なソフトウェアアーキテクチャの観点から非常によく述べていると思います。
Werner Altewischer、2018

14

他の答えは素晴らしいですが、私にとっては答えは構造的です。シングルトンにあるこのようなメソッドがあります:

- (void) dispatchOnHighPriorityNonMainQueue:(simplest_block)block forceAsync:(BOOL)forceAsync {
    if (forceAsync || [NSThread isMainThread])
        dispatch_async_on_high_priority_queue(block);
    else
        block();
}

これには2つの依存関係があります。

static void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}

そして

typedef void (^simplest_block)(void); // also could use dispatch_block_t

このようにして、他のスレッドにディスパッチするための呼び出しを集中化します。


12

dispatch_get_current_queueそもそもの使用には注意が必要です。ヘッダーファイルから:

デバッグとロギングの目的でのみ推奨されます:

返されたキューがグローバルキューの1つである場合、またはコード自体が作成したキューでない限り、コードは返されたキューについていかなる仮定もしてはなりません。キューがdispatch_get_current_queue()によって返されたものでない場合、コードは、キューへの同期実行がデッドロックから安全であると想定してはなりません。

次の2つのいずれかを実行できます。

  1. 最初に投稿したキューへの参照を保持し(で作成した場合dispatch_queue_create)、それ以降はそれを使用します。

  2. を介してシステム定義のキューを使用しdispatch_get_global_queue、どのキューを使用しているかを追跡します。

事実上、以前にシステムに依存して自分がいるキューを追跡している間、自分でそれを行う必要があります。


16
どのdispatch_get_current_queue()キューであるかを見つけるのに使用できない場合、どのようにして「最初に投稿したキューへの参照を保持する」ことができますか?実行中のキューを知る必要があるコードが、それを制御または認識していない場合があります。バックグラウンドキューで実行できる(そして実行する必要がある)コードがたくさんありますが、時々gui(進行状況バーなど)を更新する必要があるため、これらの操作のためにメインキューにディスパッチする必要があります。すでにメインキューにある場合、dispatch_sync()は永久にロックします。このためにコードをリファクタリングするには、数か月かかります。
Abhi Beckert、

3
NSURLConnectionは、呼び出し元と同じスレッドで完了コールバックを提供すると思います。コールバック時に使用するために呼び出されたキューを格納するために、同じAPI「dispatch_get_current_queue」を使用しますか?
defactodeity 2013年

5

Appleは非推奨dispatch_get_current_queue()でしたが、他の場所に穴を残したため、現在のディスパッチキューを取得できます。

if let currentDispatch = OperationQueue.current?.underlyingQueue {
    print(currentDispatch)
    // Do stuff
}

これは少なくともメインキューで機能します。このunderlyingQueueプロパティはiOS 8以降で使用できます。

元のキューで完了ブロックを実行する必要がある場合は、OperationQueueGCDなしで直接使用することもできます。



0

これは私も答えです。それでは、ユースケースについてお話します。

(他のレイヤーの中で)サービスレイヤーとUIレイヤーがあります。サービス層はバックグラウンドでタスクを実行します。(データ操作タスク、CoreDataタスク、ネットワーク呼び出しなど)。サービス層には、UI層のニーズを満たすために2つの操作キューがあります。

UIレイヤーは、サービスレイヤーに依存して機能し、成功完了ブロックを実行します。このブロックには、UIKitコードを含めることができます。簡単な使用例は、サーバーからすべてのメッセージを取得し、コレクションビューを再読み込みすることです。

ここでは、サービス層に渡されるブロックが、サービスが呼び出されたキューでディスパッチされることを保証します。dispatch_get_current_queueは非推奨のメソッドであるため、NSOperationQueue.currentQueueを使用して、呼び出し元の現在のキューを取得します。このプロパティに関する重要な注意事項。

実行中の操作のコンテキスト外からこのメソッドを呼び出すと、通常はnilが返されます。

私たちは常に既知のキュー(私たちのカスタムキューとメインキュー)でサービスを呼び出すため、これはうまく機能します。serviceAがserviceBを呼び出し、serviceBがserviceCを呼び出すことができる場合があります。最初のサービス呼び出しがどこから行われるかを制御するため、残りのサービスは同じルールに従うことがわかります。

したがって、NSOperationQueue.currentQueueは常にキューの1つまたはMainQueueを返します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.