背景
实际开发过程中经常会在block回调中拿到数据后做进一步处理,业务复杂的可能会将回调一层层套下去,比如伪代码会这样:
A handleWithCompletionBlock:^{
B handleWithCompletionBlock:^{
C handleWithCompletionBlock:^{
}
}
}
这样的情景很多,比如三个网络请求,下一个网络请求需要用到上一个回调的数据作为请求参数,但是如果直接这样嵌套的这样写,会有一些弊端:比如,(1)代码可读性较差(2)另外如果回调层数多起来的话(或者说压根就不知道有多少层),这样的写法也不现实。
解决方案
碰到类似的情况,我们可以用信号量来解决。信号量主要有3个函数,分别是:
-
创建信号量,参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_create(信号量值)
-
等待降低信号量
dispatch_semaphore_wait(信号量,等待时间)
-
提高信号量
dispatch_semaphore_signal(信号量)
注意,正常的使用顺序是先降低然后再提高,这两个函数通常成对使用。
简单的例子
我们模拟下场景,用如下3个print方法,3个print方法都加在全局队列里,异步开启各自的线程执行代码。
- (void)print1WithCompletion:(void(^)(NSString *previousPrint))completion {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *log = @"------> print 1";
completion(log);
});
}
- (void)print2WithPrint1Info:(NSString *)print1Info completion:(void(^)(NSString *previousPrint))completion {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *log = [NSString stringWithFormat:@"%@------> print 2",print1Info];
completion(log);
});
}
- (void)print3WithPrint2Info:(NSString *)print2Info completion:(void(^)(void))completion {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *log = [NSString stringWithFormat:@"%@------> print 3",print2Info];
NSLog(@"%@",log);
completion();
});
}
@property (nonatomic, strong) dispatch_semaphore_t semaphore;
self.semaphore = dispatch_semaphore_create(1);
利用信号量处理
print2拿到print1的信息,print3拿到print2的信息,最终print3输出全部的信息,通过这个场景,我们来模拟3个网络请求的嵌套。接下来我们用信号量来处理。
- (void)startPrintBySemaphore {
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
__block NSString *print1Info = nil;
[self print1WithCompletion:^(NSString *previousPrint) {
print1Info = previousPrint;
dispatch_semaphore_signal(self.semaphore);
}];
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
__block NSString *print2Info = nil;
[self print2WithPrint1Info:print1Info completion:^(NSString *previousPrint) {
print2Info = previousPrint;
dispatch_semaphore_signal(self.semaphore);
}];
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
[self print3WithPrint2Info:print2Info completion:^{
dispatch_semaphore_signal(self.semaphore);
}];
}
注意点
- 这里设置了dispatch_semaphore_create(1),这个根据自己的实际需求定
- 调用了dispatch_semaphore_wait就会使信号量-1,只要信号量>=0都能正常执行代码,不会阻塞进程
- dispatch_semaphore_signal会使信号量+1,可以理解为:我执行好了,通道恢复正常增加了1条。(这里总共的通道就是1条)
- dispatch_semaphore_signal(self.semaphore);要放在业务逻辑的后面
参考链接
另外
- 也可以通过NSOperation addDependency 来实现,具体参考* iOS多线程:『NSOperation、NSOperationQueue』详尽总结
网友评论