iOS——请求依赖多种解决方案

  • iOS开发中如何解决网络请求的依赖关系(同时应用于业务层)

    • 比如:一个(或多个)接口的请求需要依赖于另一个(或多个)网络请求的结果?
  • 目录

    • 操作依赖 – NSOperation
    • 逻辑判断 – if/else
    • 线程同步 – 组队列
    • 线程同步 – 阻塞任务
    • 线程同步 – 信号量机制

操作依赖 – NSOperation

NSOperation 操作依赖和优先级(不适用,异步网络请求并不是立刻返回,无法保证回调时再开启下一个网络请求)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Do any additional setup after loading the view, typically from a nib.
//创建队列
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
//创建操作
NSBlockOperation *operation1=[NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"执行第1次操作,线程:%@",[NSThread currentThread]);
}];
NSBlockOperation *operation2=[NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"执行第2次操作,线程:%@",[NSThread currentThread]);
}];
NSBlockOperation *operation3=[NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"执行第3次操作,线程:%@",[NSThread currentThread]);
}];
//添加依赖
[operation1 addDependency:operation2];
[operation2 addDependency:operation3];
//将操作添加到队列中去
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];

逻辑判断 – if/else

上一个网络请求的响应回调后,下一网络请求的才开始执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
NSString *urlString = @"http://www.icocos.cn";

AFHTTPSessionManager *manger =[AFHTTPSessionManager manager];

[manger GET:urlString parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {

} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"成功");
/////////////////////////////////////////////
// TODO: 执行下一个请求
/////////////////////////////////////////////
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"%@",error);
}];

但是这样会存在一个概率性的问题,就会有可能根本拿不到结果,或者由于网络慢和用户操作之间的关系导致不可预料的问题。

线程同步 – 组队列(dispatch_group)

dispatch_group是GCD(Grand Central Dispatch)中的一组方法,他有一个组的概念,可以把相关的任务归并到一个组内来执行,通过监听组内所有任务的执行情况来做相应处理。

  • 1.dispatch_group_async
    • 将代码块dispatch_block_t block放入队列dispatch_queue_t queue中执行;并和调度组dispatch_group_t group相互关联;如果提交到dispatch_queue_t queue中的block全都执行完毕会调用dispatch_group_notify并且dispatch_group_wait会停止等待;
  • 2.dispatch_group_enter(group)、dispatch_group_leave(group)
    • 和内存管理的引用计数类似,我们可以认为group也持有一个整形变量(只是假设),当调用enter时计数加1,调用leave时计数减1,当计数为0时会调用dispatch_group_notify并且dispatch_group_wait会停止等待;
  • 3.dispatch_group_notify
    • 当关联到dispatch_group_t上的dispatch_group_async任务执行完毕或者是关联在上面的dispatch_group_enter、dispatch_group_leave成对出现了。参数中的dispatch_block_t block会被提交到dispatch_queue_t queue中执行。
  • 4.dispatch_group_wait
    • 和dispatch_group_notify功能类似(多了一个dispatch_time_t参数可以设置超时时间),在group上任务完成前,dispatch_group_wait会阻塞当前线程(所以不能放在主线程调用)一直等待;当group上任务完成,或者等待时间超过设置的超时时间会结束等待;
  • 5………..
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bashdispatch_queue_t dispatchQueue = dispatch_queue_create("icocos.queue.next", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_group_t dispatchGroup = dispatch_group_create();
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){

dispatch_async(globalQueue, ^{

sleep(5);
NSLog(@"请求任务一完成");
});
});
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){

dispatch_async(globalQueue, ^{

sleep(8);
NSLog(@"请求任务二完成");
});
});
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
NSLog(@"notify:请求任务都完成了");
});
}

线程同步 –阻塞任务(dispatch_barrier):

一个dispatch barrier 允许在一个并发队列中创建一个同步点。当在并发队列中遇到一个barrier, 他会延迟执行barrier的block,等待所有在barrier之前提交的blocks执行结束。 这时,barrier block自己开始执行。 之后, 队列继续正常的执行操作。

调用这个函数总是在barrier block被提交之后立即返回,不会等到block被执行。当barrier block到并发队列的最前端,他不会立即执行。相反,队列会等到所有当前正在执行的blocks结束执行。到这时,barrier才开始自己执行。所有在barrier block之后提交的blocks会等到barrier block结束之后才执行。

  • dispatch_barrier_async函数的作用
    • 1.实现高效率的数据库访问和文件访问
    • 2.避免数据竞争
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 创建并发队列 */
dispatch_queue_t concurrentQueue = dispatch_queue_create("test.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
/* 添加两个并发操作A和B,即A和B会并发执行 */
dispatch_async(concurrentQueue, ^(){
NSLog(@"请求OperationA");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"请求OperationB");
});
/* 添加barrier障碍操作,会等待前面的并发操作结束,并暂时阻塞后面的并发操作直到其完成 */
dispatch_barrier_async(concurrentQueue, ^(){
NSLog(@"请求OperationBarrier!");
});
/* 继续添加并发操作C和D,要等待barrier障碍操作结束才能开始 */
dispatch_async(concurrentQueue, ^(){
NSLog(@"请求OperationC");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"请求OperationD");
});

线程同步 – 信号量机制(dispatch_semaphore):

  • 信号量:
    • 就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。

其实,这有点类似锁机制了,只不过信号量都是系统帮助我们处理了,我们只需要在执行线程之前,设定一个信号量值,并且在使用时,加上信号量处理方法就行了。

信号量主要有3个函数
创建信号量,参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_create(信号量值)

//等待降低信号量
dispatch_semaphore_wait(信号量,等待时间)

//提高信号量
dispatch_semaphore_signal(信号量)

注意,正常的使用顺序是先降低然后再提高,这两个函数通常成对使用。 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
- (void)getToken
{
//以上请求的设置忽略
NSURLSessionDataTask *task = [mySession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
NSLog(@"get Token");
//拿到token,传给request请求做参数
[self request:token];
}else{
NSLog(@"token error:%@",error.description);
}
}];
[task resume];
}

- (void)request:(NSString *)params
{
//请求的设置忽略
NSURLSessionDataTask *task = [mySession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
NSLog(@"request success");
}else{
NSLog(@"request error:%@----",error.description);
}
}];
[task resume];
}

// 指定调用
- (IBAction)buttonPress:(UIButton *)sender
{
//创建一个并行队列
dispatch_queue_t queque = dispatch_queue_create("GoyakodCreated", DISPATCH_QUEUE_CONCURRENT);
//异步执行
dispatch_async(queque, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self getToken:semaphore];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[self request];
});

NSLog(@"main thread");
}

- (void)getToken:(dispatch_semaphore_t)semaphore
{
//以上请求的设置忽略
NSURLSessionDataTask *task = [mySession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
NSLog(@"get Token");
//成功拿到token,发送信号量:
dispatch_semaphore_signal(semaphore);
}else{
NSLog(@"token error:%@",error.description);
}
}];
[task resume];
}
坚持原创技术分享,您的支持将鼓励我继续创作!