信号量(Semaphore)在GCD编程中是一个重要的概念,也是一个容易被忽略的概念。如果使用得当,可以巧妙地解决app开发中一些“头疼”的问题。本文会基于一个常见的场景来探讨如何合理使用信号量。
业务场景:
app实现了实现直播功能,用户点击直播按钮前,app会要求获取用户的麦克风和相机权限,如果用户同意了这两个权限,则进入直播页面;否则,app弹出alert窗口提示用户去系统设置里打开对应的权限。
面对这个“简单”的需求,作为写过很多次权限获取的iOS程序员,自然而然地写了如下代码(此处采用本人写的权限框架来获取,点此了解):
初次实现
__block BOOL micGranted = NO;
__block BOOL cameraGranted = NO;
//获取麦克风权限
[[LFPermissionMgr sharedInstance] accessMic:^(BOOL granted) {
micGranted = granted;
if (granted) {
NSLog(@"mic granted");
} else {
NSLog(@"mic not granted");
}
}];
//获取摄像头权限
[[LFPermissionMgr sharedInstance] accessCamera:^(BOOL granted) {
cameraGranted = granted;
if (granted) {
NSLog(@"camera granted");
} else {
NSLog(@"camera not granted");
}
}];
if (micGranted && cameraGranted) {
NSLog(@"start live succeed");
[self startLiveVC]; //进入直播页面
} else {
NSLog(@"start live failed");
[self showAlertView]; //弹出alert,提示用户打开系统权限
}
然而,当app运行,你发现系统权限窗口是弹了,但是console直接输出了“start live failed”,说明程序直接进入到了NSLog(@"start live failed");
,然而此时我们还没有对权限弹出窗口做权限选择。这种写法有问题。
为什么会这样?因为系统提供的权限获取函数是异步(asynchronous
)执行的,还没等用户操作,就直接往下执行了。由于micGranted
和cameraGranted
都为NO
,自然而然,进入了else
的逻辑。但是我们的需求是只有当用户操作了前面的两个权限获取后,我们才判断是否进入直播页面还是提示用户打开系统权限。这个问题可以简化成:如何等待异步block执行结束,根据处理结果,再执行其它代码?
这时候信号量就可以派上用场了。我们可以通过dispatch_semaphore_t
获取信号量,然后通过对信号量的操作,完美地解决这个问题。
对于单个block执行的一般用法
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[self methodWithABlock:^(id result){
//put code here
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
对于多个block执行的一般用法
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[self methodWithABlock:^(id result){
//put code here
dispatch_semaphore_signal(sem);
}];
[self methodWithABlock:^(id result){
//put code here
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
所以针对我们的需求,代码作如下修改
采用Semaphore的实现
__block BOOL micGranted = NO;
__block BOOL cameraGranted = NO;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
//获取麦克风权限
[[LFPermissionMgr sharedInstance] accessMic:^(BOOL granted) {
micGranted = granted;
if (granted) {
NSLog(@"mic granted");
} else {
NSLog(@"mic not granted");
}
dispatch_semaphore_signal(semaphore);
}];
//获取摄像头权限
[[LFPermissionMgr sharedInstance] accessCamera:^(BOOL granted) {
cameraGranted = granted;
if (granted) {
NSLog(@"camera granted");
} else {
NSLog(@"camera not granted");
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (micGranted && cameraGranted) {
[self startLiveVC];
} else {
[self showAlertView];
}
编译运行,会发现app执行和我们预想的一致:先弹出麦克风权限获取窗口,然后再弹出摄像头权限获取窗口,根据我们的操作,分别执行打开直播页面或者弹出权限alert窗口。
结论
掌握信号量的使用是一个iOS开发者的基本功。本文探讨了在一个具体业务场景下对信号量的使用方法,限于篇幅,没有讨论具体的理论概念。信号量是计算机科学的一个基本概念,不仅在iOS开发中,在各种语言开发中一般都会涉及。有兴趣的同学可以进一步了解相关使用方法,根据自身业务需求,举一反三。