私のアプリにはシナリオがあり、メソッドでUIの更新だけでなくデータ処理も含む時間のかかるタスクを実行したいと考えています。私の方法はこのように見えますが、
- (void)doCalculationsAndUpdateUIs {
// DATA PROCESSING 1
// UI UPDATE 1
// DATA PROCESSING 2
// UI UPDATE 2
// DATA PROCESSING 3
// UI UPDATE 3
}
時間がかかるので、バックグラウンドスレッドでデータ処理を行いたい、
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
しかし、データ処理とUI更新の両方が同じメソッドであるため、メインスレッドのUI更新のみを使用して、
dispatch_async(dispatch_get_main_queue(), ^{
最後に、私のメソッドは次のようになります。
- (void)doCalculationsAndUpdateUIs {
// DATA PROCESSING 1
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 1
});
/* I expect the control to come here after UI UPDATE 1 */
// DATA PROCESSING 2
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 2
});
/* I expect the control to come here after UI UPDATE 2 */
// DATA PROCESSING 3
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 3
});
}
これは本当に機能しますか?これは本当に良い習慣ですか?これを達成する最良の方法は何ですか?
追伸これら3つの操作はすべて相互に関連しています。
編集:申し訳ありません。 上記のコードで行が抜けていた。実際のコードは次のようになります。
- (void)doCalculationsAndUpdateUIs {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// DATA PROCESSING 1
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 1
});
/* I expect the control to come here after UI UPDATE 1 */
// DATA PROCESSING 2
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 2
});
/* I expect the control to come here after UI UPDATE 2 */
// DATA PROCESSING 3
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 3
});
});
}
もう一度、私は本当に混乱をおaびします。
いいえ、待つことはありません。そのサンプルでのあなたのやり方は良い習慣ではありません。
dispatch_async
は常に非同期です。すべてのUIブロックを同じキューにエンキューしているため、異なるブロックが順番に実行されますが、データ処理コードと並列に実行されます。
更新を待つ場合は、代わりにdispatch_sync
を使用できます。
// This will wait to finish
dispatch_sync(dispatch_get_main_queue(), ^{
// Update the UI on the main thread.
});
別のアプローチは、ブロックをキューに入れ子にすることです。しかし、私は複数のレベルには推奨しません.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Background work
dispatch_async(dispatch_get_main_queue(), ^{
// Update UI
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Background work
dispatch_async(dispatch_get_main_queue(), ^{
// Update UI
});
});
});
});
UIを更新して待つ必要がある場合は、同期バージョンを使用する必要があります。バックグラウンドスレッドがメインスレッドを待機することはまったく問題ありません。 UIの更新は非常に高速である必要があります。
計算を実行するブロックにメインキューのディスパッチを配置する必要があります。例(ここではディスパッチキューを作成し、グローバルキューは使用しません):
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
// Do some computation here.
// Update UI after computation.
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI on the main thread.
});
});
もちろん、キューを作成する場合、6.0より前のiOSバージョンをターゲットにしている場合は、dispatch_release
を忘れないでください。
提案されたdoCalculationsAndUpdateUIs
はデータ処理を実行し、UIの更新をメインキューにディスパッチします。最初に呼び出したときに、doCalculationsAndUpdateUIs
をバックグラウンドキューにディスパッチしたと仮定します。
技術的には問題ありませんが、それは少し壊れやすく、呼び出すたびにバックグラウンドにディスパッチすることを忘れないでください:代わりに、同じ内からバックグラウンドにディスパッチし、メインキューにディスパッチすることをお勧めしますロジックを明確かつ堅牢にするなどの方法.
したがって、次のようになります。
- (void)doCalculationsAndUpdateUIs {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
// DATA PROCESSING 1
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATION 1
});
/* I expect the control to come here after UI UPDATION 1 */
// DATA PROCESSING 2
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATION 2
});
/* I expect the control to come here after UI UPDATION 2 */
// DATA PROCESSING 3
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATION 3
});
});
}
UIの更新をdispatch_async
で非同期にディスパッチするか(バックグラウンドプロセスがnotUIの更新を待機するか)またはdispatch_sync
(itwillUIの更新を待つ)、質問はwhy同期して実行しますか?UI更新を待機しているときにバックグラウンドプロセスを本当に遅くしますか、またはUI更新の実行中にバックグラウンドプロセスを続行しますか?.
通常、元の質問で使用したように、dispatch_async
を使用してUI更新を非同期にディスパッチします。はい、確かにコードを同期的にディスパッチする必要がある特別な状況があります(たとえば、メインキューですべての更新を実行してクラスプロパティへの更新を同期します)が、ほとんどの場合、UI更新をディスパッチするだけです非同期に実行します。コードを同期的にディスパッチすると問題が発生する可能性があります(デッドロックなど)、だらしない場合は、UIの更新を同期的にディスパッチする必要があります。そうでない場合は、非同期でディスパッチできるようにソリューションを設計する必要があります。 。
これが「これを達成するための最良の方法」であるかどうかについてのあなたの質問に答えて、解決されるビジネス問題についてこれ以上知ることなく私たちが言うことは難しいです。たとえば、このdoCalculationsAndUpdateUIs
を複数回呼び出す場合、これらが互いにステップオーバーしないようにするために、並行グローバルキューではなく独自のシリアルキューを使用する傾向があります。または、ユーザーがシーンを閉じるか、メソッドを再度呼び出したときにこのdoCalculationsAndUpdateUIs
をキャンセルする機能が必要な場合は、キャンセル機能を提供する操作キューを使用する傾向があります。それはあなたが達成しようとしているものに完全に依存します。
ただし、一般的に、複雑なタスクを非同期でバックグラウンドキューにディスパッチし、UI更新を非同期でメインキューにディスパッチするパターンは非常に一般的です。
単一の独立したキュー操作を実行する必要があり、他の同時操作に関心がない場合は、グローバル同時キューを使用できます。
dispatch_queue_t globalConcurrentQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
これは、ドキュメントで概説されているように、指定された優先度を持つ並行キューを返します。
DISPATCH_QUEUE_PRIORITY_HIGHキューにディスパッチされたアイテムは高い優先度で実行されます。つまり、デフォルトの優先度または低優先度のキューの前に実行されるようにキューがスケジュールされます。
DISPATCH_QUEUE_PRIORITY_DEFAULTキューにディスパッチされたアイテムはデフォルトの優先度で実行されます。つまり、すべての高優先度キューがスケジュールされた後、低優先度キューがスケジュールされる前に、キューの実行がスケジュールされます。
DISPATCH_QUEUE_PRIORITY_LOWキューにディスパッチされたアイテムは低優先度で実行されます。つまり、デフォルトの優先度と高優先度のキューがすべてスケジュールされた後、キューの実行がスケジュールされます。
DISPATCH_QUEUE_PRIORITY_BACKGROUNDキューにディスパッチされたアイテムはバックグラウンド優先度で実行されます。つまり、すべてのより高い優先度のキューがスケジュールされた後にキューの実行がスケジュールされ、システムはsetpriority(2)(つまり、ディスクI/Oは調整され、スレッドのスケジューリング優先度は最低値に設定されます。
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
// Do some computation here.
// Update UI after computation.
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI on the main thread.
});
});
いいえ、待ちません。
performSelectorOnMainThread:withObject:waitUntilDone:
を使用できます。
良い習慣は: Dispatch Groups
dispatch_group_t imageGroup = dispatch_group_create();
dispatch_group_enter(imageGroup);
[uploadImage executeWithCompletion:^(NSURL *result, NSError* error){
// Image successfully uploaded to S3
dispatch_group_leave(imageGroup);
}];
dispatch_group_enter(imageGroup);
[setImage executeWithCompletion:^(NSURL *result, NSError* error){
// Image url updated
dispatch_group_leave(imageGroup);
}];
dispatch_group_notify(imageGroup,dispatch_get_main_queue(),^{
// We get here when both tasks are completed
});