如何把一个异步接口在转变为同步接口?
异步的特点是执行结果通过回调方法返回,不会阻塞方法的执行。
要把异步接口转为同步接口的关键是要阻塞住当前线程直到异步回调。
这里我们可以使用信号量dispatch_semaphore。
iOS的semaphore.h中提供了信号量功能,信号量有三个操作:
1)创建一个信号量
参数是资源个数:
1. 如果要使两个线程同步,传0
2. 如果要管理有限的临界资源,传资源个数
dispatch_semaphore_t dispatch_semaphore_create(long value);
2)等待(减小)一个信号量
如果资源个数<=0,那么这个方法会一直阻塞,直到信号(signal)的到来(返回值是0),
或者timeout结束(返回值是非零值)
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
3)发送一个信号(信号量+1)
信号量+1,如果之前信号量<=0,那么唤醒一个等待这个信号量的线程。
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);
有了信号量,就可以把一个异步方法转为同步方法:
1、创建一个信号量(参数传0)。
2、函数执行后调用dispatch_semaphore_wait进行等待,线程会一直阻塞,直到被回调唤醒。
3、在回调函数中调用dispatch_semaphore_signal,唤醒阻塞的线程。
4、阻塞的线程被唤醒,同步返回。
举一个网络请求的例子:
- (NSData *)sendSynchronousRequest:(NSURLRequest *)request forSession:(NSURLSession *)session {
if (!request) {
return nil;
}
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block NSData* data = nil;
NSURLSessionTask* task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable sData, NSURLResponse * _Nullable sResponse, NSError * _Nullable sError) {
data = sData;
dispatch_semaphore_signal(semaphore);
}];
[task resume];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[task cancel];
[session invalidateAndCancel];
return data;
}