我为什么要用信号量?
因为一个需求。
需求介绍
这是一个很常见的需求:项目中的业务接口请求的时候需要Token验证。我们最简化这个需求就是:两个请求,请求1成功返回所需参数之后,才能开始请求2。
一个很容易想到的做法就是:
- (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];
}
这种做法是最容易想到的,但是缺点也是很明显的。获取token的方法与业务层的请求紧密混合在一起了,而且很不容易分离,这会导致你的网络管理类的请求方法变得臃肿。
所以,下面我要用信号量使得token跟业务层的request完成分离开,使代码逻辑更清晰。
dispatch_semaphore 介绍
dispatch_semaphore只有三个方法:
//创建信号量
dispatch_semaphore_create
//发送信号量
dispatch_semaphore_signal
//等待信号量
dispatch_semaphore_wait
执行dispatch_semaphore_create 会根据传入的long型参数创建对应数目的信号量;执行 dispatch_semaphore_signal 会增加一个信号量;执行dispatch_semaphore_wait 则会减少一个信号量,如果信号量是0,就会根据传入的等待时间来等待。
这样一解释,不知道你有没有醍醐灌顶。对于上面的需求,我们在请求token的时候创建信号量为0,成功的话发送信号量,在业务层请求前永久等待信号即可。
看代码:
//请求的按钮点击
- (IBAction)buttonPress:(UIButton *)sender
{
//创建信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self getToken:semaphore];
//此时的信号量为0,只有token请求成功发送信号量之后,才会往下执行[self request]方法,否则会一直等下去;
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[self request];
}
- (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];
}
思路理顺了吧~让我们来跑一下程序:
哎呀~卡住了!。。。
是的,因为我们设置了永久等待,所以token请求异步去请求,在当前线程信号量是0,就不会往下执行了,会一直这样卡下去,该怎么解决呢?没错,用异步嘛~
再来代码:
- (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");
}
再跑一下程序,看控制台打印信息:
如果你还不放心可以多试几次,看看是不是token每次都在request之前。
总结
我们之所以要这样做,其实只有一个目的就是让代码清晰,这样获取Token的方法可以单独提出一个类,判断token是否过期等逻辑就不会跟业务请求混在一起了,毕竟真正项目运用的时候网络请求类要比demo复杂得多。