在开发中我们经常会遇到异步方法,在设计程序逻辑的时候有些操作依赖于异步的回调结果,有时候我们不得不把一个
原本内聚的逻辑
通过代理或者回调的方式打散
开来,这样作它打乱了我们代码顺序执行的流程。如果这个方法是同步的就好了
本篇文章会介绍针如何将异步API或者接口封装成同步方法(使用GCD的方式)
方法调用者希望调用的是一个同步返回的方法,而不是以callback形式携带返回值的方法。
// 同步方法
- (NSInteger)methodSync {
__block NSInteger result = 0;
[self methodAsync:^(NSInteger value) {
result = value;
}];
// 如果result同步返回就好了
return result;
}
// 异步方法
- (void)methodAsync:(void(^)(NSInteger result))callBack {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"methodAsync 异步开始");
sleep(2);
NSLog(@"methodAsync 异步结束");
if (callBack) {
callBack(5);
}
});
}
GCD是我们平常开发中使用频率最高的多线程方式,对于处理上述问题当然也是不在话下
-
方式一:使用派发组
dispatch_group
dispatch_group可以很方便的管理多个派发任务,并在任务结束时候可以得到回调通知,或者可以一直阻塞线程直到派发组内的所有派发任务都完成
- (NSInteger)methodSync {
NSLog(@"methodSync 开始");
__block NSInteger result = 0;
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self methodAsync:^(NSInteger value) {
result = value;
dispatch_group_leave(group);
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"methodSync 结束 result:%ld", (long)result);
return result;
}
-
方式二(推荐):使用信号量
dispatch_semaphore
dispatch_semaphore通常是用来多线程执行多个并发任务时通过信号量的方式对并发执行数量的限制。当信号量不够(=0)的时候当前调用线程将被阻塞,所以我们可以通过模拟信号量不够的情况来阻塞同步方法的返回,直到方法内部的异步回调之后;
- (NSInteger)methodSync {
NSLog(@"methodSync 开始");
__block NSInteger result = 0;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[self methodAsync:^(NSInteger value) {
result = value;
dispatch_semaphore_signal(sema);
}];
// 这里本来同步方法会立即返回,但信号量=0使得线程阻塞
// 当异步方法回调之后,发送信号,信号量变为1,这里的阻塞将被解除,从而返回正确的结果
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
NSLog(@"methodSync 结束 result:%ld", (long)result);
return result;
}