Preface
In development, we are prone to encounter such needs. We need to make multiple network requests at the same time, and all network requests can only be completed before the next operation can be carried out.
The task of network request is submitted to the child thread for asynchronous processing, and tasks such as network requests are quickly executed. However, network requests are a task, and processing received network responses is another task. Be careful not to confuse these two processes.
If you download multiple pictures, you can only display them after downloading them. Today we will study the solution to this problem.
Solution
1. First, we create a project and then do a general approach, without doing any processing to request an interface 10 times continuously:
First create the first case in viewDidLoad.
//1. No processing UIButton *Btn1 = [UIButton buttonWithType:UIButtonTypeCustom]; = CGRectMake(100, 100, 100, 40); = [UIColor grayColor]; [Btn1 setTitle:@"noConduct" forState:UIControlStateNormal]; [Btn1 addTarget:self action:@selector(Btn1) forControlEvents:UIControlEventTouchUpInside]; [ addSubview:Btn1];
Methods to implement the first case
// -(void)Btn1{ NSString *str = @"/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"); }
Run and check out our console output:
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
Obviously, without any processing, the end is first printed out because of the asynchronous callback of the network request, and then the callback order of each network request is unordered.
2. Use GCD's dispatch_group_t
In viewDidLoad:
// UIButton *Btn2 = [UIButton buttonWithType:UIButtonTypeCustom]; = CGRectMake(100, 200, 100, 40); = [UIColor grayColor]; [Btn2 setTitle:@"group--" forState:UIControlStateNormal]; [Btn2 addTarget:self action:@selector(Btn2) forControlEvents:UIControlEventTouchUpInside]; [ addSubview:Btn2];
accomplish:
//-- -(void)Btn2{ NSString *str = @"/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"); }); }
Run and see the console output:
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
From the previous two outputs, we can see that end is indeed output after all network requests, which is also in line with our needs.
We only added 4 lines of code
dispatch_group_t downloadGroup = dispatch_group_create(); dispatch_group_enter(downloadGroup); dispatch_group_leave(downloadGroup); dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ });
The above 4 lines of code can be understood as: create a dispatch_group_t. Dispatch_group_enter is first dispatch_enter before each network request, and then dispatch_group_leave after requesting a callback. For enter and leave, it must be used together. There are several times when the enter is available, otherwise the group will always exist. When all the blocks of enters are left, the block of dispatch_group_notify will be executed.
3. Use GCD's semaphore dispatch_semaphore_t
// UIButton *Btn3 = [UIButton buttonWithType:UIButtonTypeCustom]; = CGRectMake(100, 300, 100, 40); = [UIColor grayColor]; [Btn3 setTitle:@"semaphore" forState:UIControlStateNormal]; [Btn3 addTarget:self action:@selector(Btn3) forControlEvents:UIControlEventTouchUpInside]; [ addSubview:Btn3];
//-- -(void)Btn3{ NSString *str = @"/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"); }); }
Run and see the console output:
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
From the output, we can see that this method also meets our needs. In this method, we use
dispatch_semaphore_t sem = dispatch_semaphore_create(0); dispatch_semaphore_signal(sem); dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
The code of these three sentences can be understood as follows: the dispatch_semaphore semaphore is a multithreaded synchronization mechanism based on counters. If the semaphore count is greater than or equal to 1, count -1, return, and the program continues to run. If the count is 0, wait.dispatch_semaphore_signal(semaphore)
For count +1 operation,dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)
To set the waiting time, the waiting time set here is to wait all the time.
The above code is common, start with 0, wait, wait for 10 network requests to be completed, dispatch_semaphore_signal(semaphore) is count +1, then count -1 and return, and the program continues to execute. (This is why there is a count variable, record the number of network callbacks, and then send the semaphore after 10 calls, so that the subsequent program can continue to run).
4. Consider new requirements, 10 network requests sequential callbacks.
The requirement requires sequential callbacks, that is, after the first network request is executed, the second network request callback can be executed. Simply put, the output must be 0, 1, 2, 3...9.
I also proposed this requirement based on the project I have done recently. Because of the asynchronousness of network request callbacks, although we can control the order of execution of network requests, we cannot control the order of completing callbacks. This is a bit hurt. At present, my project has found a solution, but I have not found a solution to this problem yet. I will propose it to discuss it with you. (Please ignore the network request execution, callback, and request the next interface in the callback. Discuss whether there are other methods, it is best to show the code).
Finally, I posted some NSOperation code. In order to solve the new requirements, the asynchronousness of network request callbacks cannot meet the requirements. However, if it is not a network request and other asynchronous callback method, this method is OK. You can try it.
// UIButton *Btn4 = [UIButton buttonWithType:UIButtonTypeCustom]; = CGRectMake(100, 400, 100, 40); = [UIColor grayColor]; [Btn4 setTitle:@"NSOperation" forState:UIControlStateNormal]; [Btn4 addTarget:self action:@selector(Btn4) forControlEvents:UIControlEventTouchUpInside]; [ addSubview:Btn4];
// -(void)Btn4{ NSString *str = @"/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]; //Non-network request 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 will block the current thread#warning - Never wait for an Operation in the main application thread, only wait in the second or secondary thread. Blocking the main thread will cause the application to be unable to respond to user events, and the application will also appear to be unresponsive. }
Running results:
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. Or use semaphore to complete the requirements of 4
//---order UIButton *Btn5 = [UIButton buttonWithType:UIButtonTypeCustom]; = CGRectMake(100, 500, 100, 40); = [UIColor grayColor]; [Btn5 setTitle:@"order" forState:UIControlStateNormal]; [Btn5 addTarget:self action:@selector(Btn5) forControlEvents:UIControlEventTouchUpInside]; [ addSubview:Btn5];
//--order -(void)Btn5{ NSString *str = @"/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"); }); }
Let's look at the running results:
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
Let's compare the code of 3. In 3, we only call it after the end of the loopdispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)
, when the network requests callback 10 times (that is, all callbacks are completed), the incoming semaphore is added.1:( dispatch_semaphore_signal(sem) )
,Waiting for the end, and then performing subsequent operations.
In this method, every time we traverse, we let itdispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)
, At this time the thread will wait and block the current thread untildispatch_semaphore_signal(sem)
After the call, wedispatch_semaphore_signal(sem)
It is called in the callback of the network request, so the logic of this method is:
Traversal—>Start a task—>Waiting—>Task completion semaphore plus 1—>Waiting to end, start the next task
Initiate a task—>Waiting—>Task completion semaphore plus 1—>Waiting to end, start the next task
Initiate a task—>Waiting—>Task completion semaphore plus 1—>Waiting to end, start the next task
In this loop mode, only by completing a task can we continue to do the following tasks to meet our needs.
But we also need to find such a problem. When we use this method, we can clearly feel that the time it takes for the whole process has increased greatly. Unlike in 3, we (almost) enable the task to be completed and wait for the callback. Here is a network request to issue, wait, and send a second network request after completion, wait, and then send a third one after completion. In this way, the time we wait is the sum of each callback time of 10 network requests, which greatly increases the consumption in time, and fordispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)
, it will block threads. If we need to modify the UI after the network request is completed, this method will affect our interface interaction. Next, let’s compare the time consumption of the two:
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
It can be seen that 3 takes 51.488 - 51.178 about 300ms
--- ---5 takes 52.926 - 52.190 About 700ms
So please use it with caution.
Summarize
The above is the entire content of this article. I hope that the content of this article has a certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support.