信號量(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開發中,在各種語言開發中一般都會涉及。有興趣的同學可以進一步了解相關使用方法,根據自身業務需求,舉一反三。