私は、画像をダウンロードするためにその場でキューに追加することができるバルク画像ダウンローダーを構築しようとしています、そして、進行状況とダウンロードがいつ完了したかを知ることができます。
読んでみると、キュー機能のNSOperationQueue
とネットワーク機能のNSURLSession
が私の最善策のように見えますが、この2つをタンデムで使用する方法については混乱しています。
NSOperation
のインスタンスをNSOperationQueue
に追加すると、キューに入れられます。そして、NSURLSessionDownloadTask
でダウンロードタスクを作成し、複数のタスクが必要な場合は複数作成しますが、2つをどのように組み合わせるかはわかりません。
NSURLSessionDownloadTaskDelegate
は、ダウンロードの進行状況と完了の通知に必要なすべての情報を持っているようですが、特定のダウンロードを停止し、すべてのダウンロードを停止し、ダウンロードから返されるデータを処理する必要もあります。 。
ここでのあなたの直感は正しいです。多くのリクエストを発行する場合、NSOperationQueue
に4または5のmaxConcurrentOperationCount
を指定すると非常に便利です。それがない場合、多くのリクエスト(たとえば50枚の大きな画像)を発行すると、低速のネットワーク接続(一部のセルラー接続など)で作業しているときにタイムアウトの問題が発生する可能性があります。操作キューにも他の利点(たとえば、依存関係、優先順位の割り当てなど)がありますが、同時実行の程度を制御することが主な利点です(IMHO)。
completionHandler
ベースのリクエストを使用している場合、操作ベースのソリューションの実装は非常に簡単です(通常の同時NSOperation
サブクラス実装です。同時実行の操作の設定セクションを参照してください) Operation Queues の章同時実行プログラミングガイドの詳細)。
ただし、delegate
ベースの実装を使用している場合、物事はかなり早く始まります。これは、タスクレベルのデリゲートがセッションレベルで実装されるNSURLSession
の理解可能な(ただし非常に迷惑な)機能のためです。 (それについて考えてみましょう:異なる処理を必要とする2つの異なる要求は、共有セッションオブジェクトで同じデリゲートメソッドを呼び出しています。Egad!)
デリゲートベースのNSURLSessionTask
を操作にラップすることはできますが(私や他の人が行ったと確信しています)、セッションオブジェクトに辞書を相互参照するタスク識別子を保持させるという扱いにくいプロセスが伴いますタスク操作オブジェクトでは、これらのタスクデリゲートメソッドをタスクオブジェクトに渡してから、タスクオブジェクトをさまざまなNSURLSessionTask
デリゲートプロトコルに準拠させます。 NSURLSession
はセッションでmaxConcurrentOperationCount
スタイルの機能を提供しないため、かなりの量の作業が必要です(依存関係、完了など、他のNSOperationQueue
の良さは言うまでもありません)ブロックなど)。
ただし、操作ベースの実装は、バックグラウンドセッションを備えた初心者向けではありません。アップロード/ダウンロードタスクは、アプリが終了した後も引き続き機能します(これは良いことです。これはバックグラウンドリクエストではかなり重要な動作です)が、アプリを再起動すると、操作キューとその操作がすべてなくなります。 。そのため、バックグラウンドセッションには、純粋なデリゲートベースのNSURLSession
実装を使用する必要があります。
概念的には、NSURLSessionは操作キューです。 NSURLSessionタスクと完了ハンドラーのブレークポイントを再開すると、スタックトレースが非常に明らかになります。
NSURLSession に関する忠実なRay Wenderlichのチュートリアルからの抜粋です。完了ハンドラーの実行時にブレークポイントにNSLog
ステートメントが追加されています。
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:londonWeatherUrl]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
// handle response
NSLog(@"Handle response"); // <-- breakpoint here
}] resume];
上記では、Thread 5 Queue: NSOperationQueue Serial Queue
で実行されている完了ハンドラーを確認できます。
したがって、各NSURLSessionが独自の操作キューを維持し、セッションに追加された各タスクが内部でNSOperationとして実行されると推測します。したがって、NSURLSessionオブジェクトまたはNSURLSessionタスクを制御する操作キューを維持することは意味がありません。
NSURLSessionTask自体は、cancel
、resume
、suspend
などの同等のメソッドをすでに提供しています。
独自のNSOperationQueueを使用する場合よりも制御が少ないことは事実です。ただし、NSURLSessionは新しいクラスであり、その目的は疑う余地なくその負担を軽減することです。
一番下の行:煩わしさを抑えたいが、制御を抑えたい場合、および信頼してAppleを代理してネットワークタスクを適切に実行するには、NSURLSessionを使用します。 NSURLConnectionと独自の操作キューを使用します。
更新:executing
およびfinishing
プロパティは、現在のNSOperation
のステータスに関する知識を保持します。 finishing
がYES
に設定され、executing
がNO
に設定されると、操作は完了したと見なされます。正しい処理方法はdispatch_group
を必要とせず、単に非同期NSOperation
として書くことができます:
- (BOOL) isAsynchronous {
return YES;
}
- (void) main
{
// We are starting everything
self.executing = YES;
self.finished = NO;
NSURLSession * session = [NSURLSession sharedInstance];
NSURL *url = [NSURL URLWithString:@"http://someurl"];
NSURLSessionDataTask * dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
/* Do your stuff here */
NSLog("Will show in second");
self.executing = NO;
self.finished = YES;
}];
[dataTask resume]
}
asynchronous
という用語は誤解を招くものであり、UI(メイン)スレッドとバックグラウンドスレッドの区別を意味するものではありません。
isAsynchronous
がYES
に設定されている場合、これは、コードの一部がmain
メソッドに関して非同期に実行されることを意味します。別の言い方をすると:非同期呼び出しがmain
メソッド内で行われ、メインメソッドの終了後にメソッドが終了します。
Apple os: https://speakerdeck.com/yageek/concurrency-on-darwin で並行性を処理する方法についてのスライドがあります。
古い回答:dispatch_group_t
を試すことができます。 GCDの保持カウンターとして考えることができます。
main
サブクラスのNSOperation
メソッドで以下のコードを想像してください:
- (void) main
{
self.executing = YES;
self.finished = NO;
// Create a group -> value = 0
dispatch_group_t group = dispatch_group_create();
NSURLSession * session = [NSURLSession sharedInstance];
NSURL *url = [NSURL URLWithString:@"http://someurl"];
// Enter the group manually -> Value = Value + 1
dispatch_group_enter(group); ¨
NSURLSessionDataTask * dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
/* Do your stuff here */
NSLog("Will show in first");
//Leave the group manually -> Value = Value - 1
dispatch_group_leave(group);
}];
[dataTask resume];
// Wait for the group's value to equals 0
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog("Will show in second");
self.executing = NO;
self.finished = YES;
}
NSURLSessionを使用すると、手動でキューに操作を追加することはありません。 NSURLSessionでメソッド- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
を使用して、データタスクを生成し、それを開始します(resumeメソッドを呼び出します)。
操作キューを提供して、キューのプロパティを制御し、必要に応じて他の操作に使用することもできます。
データタスクで実行するNSOperationで実行する通常のアクション(開始、一時停止、停止、再開)のいずれか。
50個の画像をダウンロードするためにキューに入れるには、NSURLSessionが適切にキューに入れる50個のデータタスクを作成するだけです。
OperationQueueを使用しており、各操作で多数の同時ネットワーク要求を作成したくない場合は、各操作がキューに追加された後にqueue.waitUntilAllOperationsAreFinished()を呼び出すだけで済みます。以前の接続が完了した後にのみ実行されるようになり、同時ネットワーク接続の量が大幅に削減されます。