iOS複数のネットワーク要求が完了したら、次のステップを実行する方法の詳細


前言
開発の中で、私達はこのような需要に出会いやすく、同時に複数のネットワーク要求を行う必要があります。すべてのネットワーク要求が完了したら、次のステップの操作ができます。
ネットワーク要求のタスクは、サブスレッド非同期処理に提出され、ネットワーク要求のようなタスクは、急速に実行されましたが、ネットワーク要求はタスクであり、受信したネットワーク応答を処理するのはまたタスクです。この2つのプロセスを混同しないように注意してください。
複数の画像をダウンロードしたら、ダウンロードして展示できます。今日はこの問題の解決策を検討します。
解決方法
1.まず、プロジェクトを作成して、一般的なやり方をして、何の処理もしないで連続して一つのインターフェースを10回要求します。
まずview DidLoadで第一の状況を作成します。

//1.   
 UIButton *Btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
 Btn1.frame = CGRectMake(100, 100, 100, 40);
 Btn1.backgroundColor = [UIColor grayColor];
 [Btn1 setTitle:@"noConduct" forState:UIControlStateNormal];
 [Btn1 addTarget:self action:@selector(Btn1) forControlEvents:UIControlEventTouchUpInside];
 [self.view addSubview:Btn1];
第一の状況を実現する方法

//1.noConduct
-(void)Btn1{
 NSString *str = @"http://www.jianshu.com/p/6930f335adba";
 NSURL *url = [NSURL URLWithString:str];
 NSURLRequest *request = [NSURLRequest requestWithURL:url];
 NSURLSession *session = [NSURLSession sharedSession];
 
 for (int i=0; i<10; i++) {
 NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  
  NSLog(@"%d---%d",i,i);
  
 }];
 
 [task resume];
 }
 
 NSLog(@"end");
}
運転してください。私たちのコンソール出力を見てください。

2017-12-04 17:10:10.503 DownImage[3289:261033] end
2017-12-04 17:10:10.676 DownImage[3289:261080] 0---0
2017-12-04 17:10:10.704 DownImage[3289:261080] 1---1
2017-12-04 17:10:10.754 DownImage[3289:261096] 4---4
2017-12-04 17:10:10.760 DownImage[3289:261080] 2---2
2017-12-04 17:10:10.800 DownImage[3289:261096] 5---5
2017-12-04 17:10:10.840 DownImage[3289:261080] 7---7
2017-12-04 17:10:10.844 DownImage[3289:261082] 6---6
2017-12-04 17:10:10.846 DownImage[3289:261096] 3---3
2017-12-04 17:10:10.888 DownImage[3289:261096] 8---8
2017-12-04 17:10:10.945 DownImage[3289:261080] 9---9
明らかに、任意の処理がない場合、endは最初に印刷され、ネットワーク要求の非同期フィードバックのため、各ネットワーク要求のコールバック順序は無秩序である。
2.GCDを使ったdispatch_グループt
view DidLoadの中:

//2.group
 UIButton *Btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
 Btn2.frame = CGRectMake(100, 200, 100, 40);
 Btn2.backgroundColor = [UIColor grayColor];
 [Btn2 setTitle:@"group--" forState:UIControlStateNormal];
 [Btn2 addTarget:self action:@selector(Btn2) forControlEvents:UIControlEventTouchUpInside];
 [self.view addSubview:Btn2];
実装:

//2.group--
-(void)Btn2{
 NSString *str = @"http://www.jianshu.com/p/6930f335adba";
 NSURL *url = [NSURL URLWithString:str];
 NSURLRequest *request = [NSURLRequest requestWithURL:url];
 NSURLSession *session = [NSURLSession sharedSession];
 
 dispatch_group_t downloadGroup = dispatch_group_create();
 for (int i=0; i<10; i++) {
 dispatch_group_enter(downloadGroup);
 
 NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  
  NSLog(@"%d---%d",i,i);
  dispatch_group_leave(downloadGroup);
  
 }];
 
 [task resume];
 }
 
 dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
 NSLog(@"end");
 });
}
実行してみてください。コンソール出力:

2017-12-04 17:14:46.984 DownImage[3289:265374] 2---2
2017-12-04 17:14:46.987 DownImage[3289:265370] 1---1
2017-12-04 17:14:47.052 DownImage[3289:265383] 5---5
2017-12-04 17:14:47.065 DownImage[3289:265370] 4---4
2017-12-04 17:14:47.111 DownImage[3289:265379] 3---3
2017-12-04 17:14:47.121 DownImage[3289:265383] 6---6
2017-12-04 17:14:47.169 DownImage[3289:265383] 7---7
2017-12-04 17:14:47.192 DownImage[3289:265370] 9---9
2017-12-04 17:14:47.321 DownImage[3289:265383] 8---8
2017-12-04 17:14:47.747 DownImage[3289:265374] 0---0
2017-12-04 17:14:47.747 DownImage[3289:261033] end

2017-12-04 17:15:14.576 DownImage[3289:265942] 3---3
2017-12-04 17:15:14.626 DownImage[3289:265936] 2---2
2017-12-04 17:15:14.647 DownImage[3289:265944] 4---4
2017-12-04 17:15:14.648 DownImage[3289:265936] 0---0
2017-12-04 17:15:14.657 DownImage[3289:265943] 1---1
2017-12-04 17:15:14.709 DownImage[3289:265944] 5---5
2017-12-04 17:15:14.728 DownImage[3289:265944] 6---6
2017-12-04 17:15:14.734 DownImage[3289:265944] 7---7
2017-12-04 17:15:14.738 DownImage[3289:265943] 8---8
2017-12-04 17:15:14.816 DownImage[3289:265944] 9---9
2017-12-04 17:15:14.816 DownImage[3289:261033] end
前回の2回の出力から、endは確かにすべてのネットワークから要求された後に出力されます。これも私たちのニーズに合っています。
コードには4行のコードしか追加されていません。

dispatch_group_t downloadGroup = dispatch_group_create();
dispatch_group_enter(downloadGroup);
dispatch_group_leave(downloadGroup);
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
 });
以上の4行のコードは、dispatch chを作成すると理解できます。グループt,毎回ネットは要求する前にまずdispatch_を使います。グループベンダー、折り返しをお願いしてから、dispatch_グループleaveは、enterとleaveに対して協力して使用しなければならない。何回かのイベントがあれば、leaveがあります。そうでなければ、グループはずっと存在します。すべてのenterのblockがleaveになったら、dispatch_を実行します。グループnotifyのblock。
3.GCDを使用した信号量dispatch_semaphoret

//3.semaphore
 UIButton *Btn3 = [UIButton buttonWithType:UIButtonTypeCustom];
 Btn3.frame = CGRectMake(100, 300, 100, 40);
 Btn3.backgroundColor = [UIColor grayColor];
 [Btn3 setTitle:@"semaphore" forState:UIControlStateNormal];
 [Btn3 addTarget:self action:@selector(Btn3) forControlEvents:UIControlEventTouchUpInside];
 [self.view addSubview:Btn3];

//3.semaphore--
-(void)Btn3{
 NSString *str = @"http://www.jianshu.com/p/6930f335adba";
 NSURL *url = [NSURL URLWithString:str];
 NSURLRequest *request = [NSURLRequest requestWithURL:url];
 NSURLSession *session = [NSURLSession sharedSession];
 
 dispatch_semaphore_t sem = dispatch_semaphore_create(0);
 for (int i=0; i<10; i++) {
 
 NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  
  NSLog(@"%d---%d",i,i);
  count++;
  if (count==10) {
  dispatch_semaphore_signal(sem);
  count = 0;
  }
  
 }];
 
 [task resume];
 }
 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
 
 dispatch_async(dispatch_get_main_queue(), ^{
 NSLog(@"end");
 });
}
実行してみてください。コンソール出力:

2017-12-04 17:36:49.098 DownImage[3428:283651] 2---2
2017-12-04 17:36:49.144 DownImage[3428:284210] 0---0
2017-12-04 17:36:49.152 DownImage[3428:284213] 3---3
2017-12-04 17:36:49.158 DownImage[3428:283651] 1---1
2017-12-04 17:36:49.167 DownImage[3428:284210] 4---4
2017-12-04 17:36:49.235 DownImage[3428:284213] 8---8
2017-12-04 17:36:49.249 DownImage[3428:283651] 5---5
2017-12-04 17:36:49.252 DownImage[3428:283651] 7---7
2017-12-04 17:36:49.324 DownImage[3428:283651] 9---9
2017-12-04 17:36:49.468 DownImage[3428:284214] 6---6
2017-12-04 17:36:49.469 DownImage[3428:283554] end

2017-12-04 17:37:11.554 DownImage[3428:284747] 0---0
2017-12-04 17:37:11.555 DownImage[3428:284733] 1---1
2017-12-04 17:37:11.627 DownImage[3428:284748] 5---5
2017-12-04 17:37:11.661 DownImage[3428:284748] 2---2
2017-12-04 17:37:11.688 DownImage[3428:284747] 4---4
2017-12-04 17:37:11.709 DownImage[3428:284747] 6---6
2017-12-04 17:37:11.770 DownImage[3428:284733] 7---7
2017-12-04 17:37:11.774 DownImage[3428:284733] 8---8
2017-12-04 17:37:11.824 DownImage[3428:284747] 9---9
2017-12-04 17:37:11.899 DownImage[3428:284733] 3---3
2017-12-04 17:37:11.900 DownImage[3428:283554] end
出力から見ると、このような方法も私達の需要を満足させるものであり、この方法では私達が使用しました。

dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_semaphore_signal(sem);
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
この三つのコードはこのように理解できます。dispatch_semaphore信号量はカウンタに基づくマルチスレッド同期機構である。semaphoreカウントが1以上の場合、カウント-1、戻り、プログラムは継続します。0をカウントすると、待機します。dispatch_semaphore_signal(semaphore)はカウント+1の動作であり、dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)は待ち時間を設定するため、ここで設定された待ち時間は待ち時間である。
以上のコードについては分かりやすく、開始は0で、待ち時間は10のネットワーク要求が全部完了しました。dispatch_semaphoresignal(semaphore)はカウント+1となり、カウント-1は戻り、プログラムは実行されます(ここではなぜcount変数があるのかというと、ネットワークのコールバックの回数を記録し、10回戻してから信号量を送り、後のプログラムを実行させます。
4.新しい需要を考慮して、10のネットワーク要求順序を調整する。
第一のネットワーク要求を実行した後、第二のネットワークリクエストのフィードバックが実行されます。簡単に言えば、出力は0,1,2,3...9です。
この需要に対して私も自分が最近やったプロジェクトによって提起したのです。インターネット要求のフィードバックの非同期性は、ネットワーク要求の順序を制御して実行することができますが、その調整順序を制御することができません。これはちょっと怪我しました。今はプロジェクトで解決案を見つけましたが、この問題はまだ解決方法が見つからないので、みんなと相談してみます。ネットワーク要求の実行を無視してください。コールバックで次のインターフェースを要求する方法がありますか?他の方法がありますか?
最後に、NSOperationのコードを貼り付けて、新しい需要を解決するために書いたものです。ネットワーク要求の非同期性では需要を満たすことができません。

//4.NSOperation
 UIButton *Btn4 = [UIButton buttonWithType:UIButtonTypeCustom];
 Btn4.frame = CGRectMake(100, 400, 100, 40);
 Btn4.backgroundColor = [UIColor grayColor];
 [Btn4 setTitle:@"NSOperation" forState:UIControlStateNormal];
 [Btn4 addTarget:self action:@selector(Btn4) forControlEvents:UIControlEventTouchUpInside];
 [self.view addSubview:Btn4];

//4.NSOperation
-(void)Btn4{
 NSString *str = @"http://www.jianshu.com/p/6930f335adba";
 NSURL *url = [NSURL URLWithString:str];
 NSURLRequest *request = [NSURLRequest requestWithURL:url];
 NSURLSession *session = [NSURLSession sharedSession];
 
 NSMutableArray *operationArr = [[NSMutableArray alloc]init];
 for (int i=0; i<10; i++) {
  NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
   NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    
    NSLog(@"%d---%d",i,i);
    
   }];
   
   [task resume];

   //     
   NSLog(@"noRequest-%d",i);
  }];
  
  [operationArr addObject:operation];
  if (i>0) {
   NSBlockOperation *operation1 = operationArr[i-1];
   NSBlockOperation *operation2 = operationArr[i];
   [operation2 addDependency:operation1];
  }
 }
 
 NSOperationQueue *queue = [[NSOperationQueue alloc]init];
 [queue addOperations:operationArr waitUntilFinished:NO]; //YES       
#warning -                Operation,             。                  ,          。
 
}
実行結果:

2017-12-04 18:03:10.224 DownImage[3584:304363] noRequest-0
2017-12-04 18:03:10.226 DownImage[3584:304362] noRequest-1
2017-12-04 18:03:10.226 DownImage[3584:304363] noRequest-2
2017-12-04 18:03:10.231 DownImage[3584:304363] noRequest-3
2017-12-04 18:03:10.232 DownImage[3584:304362] noRequest-4
2017-12-04 18:03:10.233 DownImage[3584:304362] noRequest-5
2017-12-04 18:03:10.233 DownImage[3584:304363] noRequest-6
2017-12-04 18:03:10.234 DownImage[3584:304363] noRequest-7
2017-12-04 18:03:10.235 DownImage[3584:304363] noRequest-8
2017-12-04 18:03:10.236 DownImage[3584:304363] noRequest-9
2017-12-04 18:03:10.408 DownImage[3584:304597] 2---2
2017-12-04 18:03:10.408 DownImage[3584:304597] 0---0
2017-12-04 18:03:10.409 DownImage[3584:304597] 1---1
2017-12-04 18:03:10.461 DownImage[3584:304597] 5---5
2017-12-04 18:03:10.476 DownImage[3584:304363] 4---4
2017-12-04 18:03:10.477 DownImage[3584:304365] 6---6
2017-12-04 18:03:10.518 DownImage[3584:304365] 7---7
2017-12-04 18:03:10.537 DownImage[3584:304596] 8---8
2017-12-04 18:03:10.547 DownImage[3584:304362] 9---9
2017-12-04 18:03:11.837 DownImage[3584:304362] 3---3

2017-12-04 18:04:27.699 DownImage[3584:306401] noRequest-0
2017-12-04 18:04:27.700 DownImage[3584:306405] noRequest-1
2017-12-04 18:04:27.701 DownImage[3584:306401] noRequest-2
2017-12-04 18:04:27.701 DownImage[3584:306405] noRequest-3
2017-12-04 18:04:27.702 DownImage[3584:306401] noRequest-4
2017-12-04 18:04:27.702 DownImage[3584:306405] noRequest-5
2017-12-04 18:04:27.703 DownImage[3584:306401] noRequest-6
2017-12-04 18:04:27.703 DownImage[3584:306401] noRequest-7
2017-12-04 18:04:27.704 DownImage[3584:306401] noRequest-8
2017-12-04 18:04:27.704 DownImage[3584:306401] noRequest-9
2017-12-04 18:04:27.772 DownImage[3584:306397] 2---2
2017-12-04 18:04:27.779 DownImage[3584:306401] 0---0
2017-12-04 18:04:27.782 DownImage[3584:306409] 1---1
2017-12-04 18:04:27.800 DownImage[3584:306405] 3---3
2017-12-04 18:04:27.851 DownImage[3584:306401] 6---6
2017-12-04 18:04:27.855 DownImage[3584:306397] 5---5
2017-12-04 18:04:27.915 DownImage[3584:306397] 7---7
2017-12-04 18:04:27.951 DownImage[3584:306397] 9---9
2017-12-04 18:04:27.953 DownImage[3584:306405] 8---8
2017-12-04 18:04:28.476 DownImage[3584:306409] 4---4
5.やはり信号量semaphoreを使って4の需要を完成します。

//5.semaphore---order
 UIButton *Btn5 = [UIButton buttonWithType:UIButtonTypeCustom];
 Btn5.frame = CGRectMake(100, 500, 100, 40);
 Btn5.backgroundColor = [UIColor grayColor];
 [Btn5 setTitle:@"order" forState:UIControlStateNormal];
 [Btn5 addTarget:self action:@selector(Btn5) forControlEvents:UIControlEventTouchUpInside];
 [self.view addSubview:Btn5];

//5.semaphore--order
-(void)Btn5{
 NSString *str = @"http://www.jianshu.com/p/6930f335adba";
 NSURL *url = [NSURL URLWithString:str];
 NSURLRequest *request = [NSURLRequest requestWithURL:url];
 NSURLSession *session = [NSURLSession sharedSession];
 dispatch_semaphore_t sem = dispatch_semaphore_create(0);
 for (int i=0; i<10; i++) {
  NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
   
   NSLog(@"%d---%d",i,i);
   dispatch_semaphore_signal(sem);
  }];
  [task resume];
  dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
 }
 dispatch_async(dispatch_get_main_queue(), ^{
  NSLog(@"end");
 });
}
運行結果を見てみます。

2017-12-05 10:17:28.175 DownImage[938:51296] 0---0
2017-12-05 10:17:28.331 DownImage[938:51289] 1---1
2017-12-05 10:17:28.506 DownImage[938:51289] 2---2
2017-12-05 10:17:28.563 DownImage[938:51289] 3---3
2017-12-05 10:17:28.662 DownImage[938:51289] 4---4
2017-12-05 10:17:28.733 DownImage[938:51296] 5---5
2017-12-05 10:17:28.792 DownImage[938:51296] 6---6
2017-12-05 10:17:28.856 DownImage[938:51286] 7---7
2017-12-05 10:17:29.574 DownImage[938:51289] 8---8
2017-12-05 10:17:29.652 DownImage[938:51286] 9---9
2017-12-05 10:17:29.653 DownImage[938:45252] end

2017-12-05 10:17:46.341 DownImage[938:51608] 0---0
2017-12-05 10:17:47.967 DownImage[938:51607] 1---1
2017-12-05 10:17:48.038 DownImage[938:51603] 2---2
2017-12-05 10:17:48.132 DownImage[938:51603] 3---3
2017-12-05 10:17:48.421 DownImage[938:51608] 4---4
2017-12-05 10:17:48.537 DownImage[938:51289] 5---5
2017-12-05 10:17:48.646 DownImage[938:51289] 6---6
2017-12-05 10:17:48.939 DownImage[938:51289] 7---7
2017-12-05 10:17:50.537 DownImage[938:51607] 8---8
2017-12-05 10:17:50.615 DownImage[938:51289] 9---9
2017-12-05 10:17:50.616 DownImage[938:45252] end
私たちは3のコードを比較して、3の中で最後、つまりサイクル終了後にdispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER) を呼び出します。ループ中に10回のコールを要求した場合(つまり、すべてのコールバックが完了した場合)、入ってきた信号の量を1:( dispatch_semaphore_signal(sem) )に加算します。このとき終了を待って、その後の操作を行います。
この方法では、dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)が起動した後に、dispatch_semaphore_signal(sem)がネットワーク要求のコールバックで呼び出されたので、スレッドは待ちます。
巡回->開始タスク-->タスク完了信号量1-->終了を待って、次のタスクを開始します。
タスクの開始待ち-->タスクの完了信号量を1に加算します。終了を待って、次のタスクを開始します。
タスクの開始待ち-->タスクの完了信号量を1に加算します。終了を待って、次のタスクを開始します。
このように循環するモードでは、一つの任務を達成してから次の任務を行うことができ、私達の需要を満たす。
しかし、私たちもこのような問題を発見したいです。このような方式を使うと、全体の過程にかかる時間が大幅に増えたということが分かります。私たち3の中で同時に(ほぼ)任務を開けて、折り返し調整が完了するのを待っています。このように、私たちが待つ時間は10のネットワーク要求の各々の調整時間の和であり、時間的には消費が大幅に増加し、dispatch_semaphore_signal(sem)に対しては、スレッドがブロックされます。もしネットワーク要求が完了したら、UIを変更する必要があるなら、このような方式は私たちのインターフェースに影響を与えます。次に、両者の時間消費を比較します。

3-------------3----------3-------
2017-12-05 10:29:51.178 DownImage[938:56971] 2---2
2017-12-05 10:29:51.193 DownImage[938:57200] 0---0
2017-12-05 10:29:51.202 DownImage[938:56631] 3---3
2017-12-05 10:29:51.248 DownImage[938:56971] 1---1
2017-12-05 10:29:51.262 DownImage[938:56971] 5---5
2017-12-05 10:29:51.291 DownImage[938:56631] 6---6
2017-12-05 10:29:51.375 DownImage[938:56631] 7---7
2017-12-05 10:29:51.384 DownImage[938:56631] 4---4
2017-12-05 10:29:51.434 DownImage[938:56971] 8---8
2017-12-05 10:29:51.487 DownImage[938:57199] 9---9
2017-12-05 10:29:51.488 DownImage[938:45252] end

5-------------5----------5-------
2017-12-05 10:29:52.190 DownImage[938:56631] 0---0
2017-12-05 10:29:52.304 DownImage[938:57199] 1---1
2017-12-05 10:29:52.432 DownImage[938:56971] 2---2
2017-12-05 10:29:52.520 DownImage[938:56971] 3---3
2017-12-05 10:29:52.576 DownImage[938:56631] 4---4
2017-12-05 10:29:52.628 DownImage[938:56971] 5---5
2017-12-05 10:29:52.706 DownImage[938:56631] 6---6
2017-12-05 10:29:52.764 DownImage[938:56971] 7---7
2017-12-05 10:29:52.853 DownImage[938:56631] 8---8
2017-12-05 10:29:52.925 DownImage[938:56971] 9---9
2017-12-05 10:29:52.926 DownImage[938:45252] end
3の所要時間は51.488-51.178で約300 msと見られています。
---5時間は52.926-52.190で約700 msかかります。
だから皆さんも慎重に使ってください。
締め括りをつける
以上はこの文章の全部の内容です。本文の内容は皆さんの学習や仕事に対して一定の参考となる学習価値を持っています。質問があれば、メッセージを書いて交流してください。ありがとうございます。