ブロックと完了ブロックを受け入れるメソッドがあります。最初のブロックはバックグラウンドで実行し、完了ブロックはメソッドが呼び出されたキューで実行する必要があります。
後者の場合は常に使用dispatch_get_current_queue()
していましたが、iOS 6以降では非推奨のようです。代わりに何を使用すればよいですか?
ブロックと完了ブロックを受け入れるメソッドがあります。最初のブロックはバックグラウンドで実行し、完了ブロックはメソッドが呼び出されたキューで実行する必要があります。
後者の場合は常に使用dispatch_get_current_queue()
していましたが、iOS 6以降では非推奨のようです。代わりに何を使用すればよいですか?
回答:
「発信者がいたキューで実行する」というパターンは魅力的ですが、最終的には素晴らしいアイデアではありません。そのキューは、優先度の低いキュー、メインキュー、または奇妙なプロパティを持つその他のキューである可能性があります。
これに対する私のお気に入りのアプローチは、「完了ブロックは、x、y、zのプロパティを持つ実装定義のキューで実行される」と言い、呼び出し側がそれ以上の制御を必要とする場合、ブロックを特定のキューにディスパッチさせます。指定するプロパティの典型的なセットは、「シリアル、非再入可能、および他のアプリケーションから見えるキューに関して非同期」のようなものです。
**編集**
Catfish_Manが以下のコメントに例を示しました。私は彼の答えに追加しています。
- (void) aMethodWithCompletionBlock:(dispatch_block_t)completionHandler
{
dispatch_async(self.workQueue, ^{
[self doSomeWork];
dispatch_async(self.callbackQueue, completionHandler);
}
}
これは、基本的に、説明するAPIにとって間違ったアプローチです。APIが実行するブロックと完了ブロックを受け入れる場合、次の事実が真である必要があります。
「実行するブロック」は内部キューで実行する必要があります。たとえば、API専用で、そのAPIの完全な制御下にあるキューなどです。これに対する唯一の例外は、ブロックがメインキューまたはグローバルコンカレントキューの1つで実行されることをAPIが明確に宣言している場合です。
#1と同じ仮定が成り立つ場合を除き、完了ブロックは常にタプル(キュー、ブロック)として表現する必要があります。たとえば、完了ブロックは既知のグローバルキューで実行されます。さらに、完了ブロックは、渡されたキューで非同期にディスパッチされる必要があります。
これらは単なる文体上のポイントではなく、APIがデッドロックや、いつか最も近いツリーからハングする他のエッジケースの動作から安全である場合に完全に必要です。:-)
他の答えは素晴らしいですが、私にとっては答えは構造的です。シングルトンにあるこのようなメソッドがあります:
- (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
このようにして、他のスレッドにディスパッチするための呼び出しを集中化します。
dispatch_get_current_queue
そもそもの使用には注意が必要です。ヘッダーファイルから:
デバッグとロギングの目的でのみ推奨されます:
返されたキューがグローバルキューの1つである場合、またはコード自体が作成したキューでない限り、コードは返されたキューについていかなる仮定もしてはなりません。キューがdispatch_get_current_queue()によって返されたものでない場合、コードは、キューへの同期実行がデッドロックから安全であると想定してはなりません。
次の2つのいずれかを実行できます。
最初に投稿したキューへの参照を保持し(で作成した場合dispatch_queue_create
)、それ以降はそれを使用します。
を介してシステム定義のキューを使用しdispatch_get_global_queue
、どのキューを使用しているかを追跡します。
事実上、以前にシステムに依存して自分がいるキューを追跡している間、自分でそれを行う必要があります。
dispatch_get_current_queue()
キューであるかを見つけるのに使用できない場合、どのようにして「最初に投稿したキューへの参照を保持する」ことができますか?実行中のキューを知る必要があるコードが、それを制御または認識していない場合があります。バックグラウンドキューで実行できる(そして実行する必要がある)コードがたくさんありますが、時々gui(進行状況バーなど)を更新する必要があるため、これらの操作のためにメインキューにディスパッチする必要があります。すでにメインキューにある場合、dispatch_sync()は永久にロックします。このためにコードをリファクタリングするには、数か月かかります。
Appleは非推奨dispatch_get_current_queue()
でしたが、他の場所に穴を残したため、現在のディスパッチキューを取得できます。
if let currentDispatch = OperationQueue.current?.underlyingQueue {
print(currentDispatch)
// Do stuff
}
これは少なくともメインキューで機能します。このunderlyingQueue
プロパティはiOS 8以降で使用できます。
元のキューで完了ブロックを実行する必要がある場合は、OperationQueue
GCDなしで直接使用することもできます。
キューを比較する必要がある場合は、ラベルまたは指定によってキューを比較できます。これをチェックしてくださいhttps://stackoverflow.com/a/23220741/1531141
これは私も答えです。それでは、ユースケースについてお話します。
(他のレイヤーの中で)サービスレイヤーとUIレイヤーがあります。サービス層はバックグラウンドでタスクを実行します。(データ操作タスク、CoreDataタスク、ネットワーク呼び出しなど)。サービス層には、UI層のニーズを満たすために2つの操作キューがあります。
UIレイヤーは、サービスレイヤーに依存して機能し、成功完了ブロックを実行します。このブロックには、UIKitコードを含めることができます。簡単な使用例は、サーバーからすべてのメッセージを取得し、コレクションビューを再読み込みすることです。
ここでは、サービス層に渡されるブロックが、サービスが呼び出されたキューでディスパッチされることを保証します。dispatch_get_current_queueは非推奨のメソッドであるため、NSOperationQueue.currentQueueを使用して、呼び出し元の現在のキューを取得します。このプロパティに関する重要な注意事項。
実行中の操作のコンテキスト外からこのメソッドを呼び出すと、通常はnilが返されます。
私たちは常に既知のキュー(私たちのカスタムキューとメインキュー)でサービスを呼び出すため、これはうまく機能します。serviceAがserviceBを呼び出し、serviceBがserviceCを呼び出すことができる場合があります。最初のサービス呼び出しがどこから行われるかを制御するため、残りのサービスは同じルールに従うことがわかります。
したがって、NSOperationQueue.currentQueueは常にキューの1つまたはMainQueueを返します。
dispatch_get_current_queue()
iOS 6で非推奨となったのはなぜですか?ドキュメントは、それについて何も言わない