蓝色小药丸 2017-12-17
我为什么要用信号量?因为一个需求。
##需求介绍
这是一个很常见的需求:项目中的业务接口请求的时候需要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复杂得多。
##最后demo 地址