iOS开发文集iOS Developer移动开发

如何“优雅”使用GCD Semaphore

2016-10-20  本文已影响64人  la0fu

信号量(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)执行的,还没等用户操作,就直接往下执行了。由于micGrantedcameraGranted都为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开发中,在各种语言开发中一般都会涉及。有兴趣的同学可以进一步了解相关使用方法,根据自身业务需求,举一反三。

下载代码

上一篇下一篇

猜你喜欢

热点阅读