問題和需求
APP在啟動完成后,一般會在首頁有多個彈窗,這些彈窗大多都是自定義的,網(wǎng)絡(luò)回來的時機也不一定。因此多個彈窗如果不做處理就會一起全部彈出來,這樣會比較影響體驗,如果沒有好的管理方法,那么維護就變得困難。
先附上效果對比圖

這個效果是通過信號量實現(xiàn)的
信號量概念
信號量是用于多線程同步的,跟鎖不一樣的是,信號量不一定是鎖定某一個資源,而是流程上的概念,比如:有A,B兩個線程,B線程要等A線程完成某一任務(wù)以后再進行自己下面的步驟,這個任務(wù) 并不一定是鎖定某一資源,還可以是進行一些計算或者數(shù)據(jù)處理之類。而對于鎖來說,鎖住的資源無法被其余的線程訪問,從而阻塞線程而實現(xiàn)線程同步。
實現(xiàn)
首先創(chuàng)建一個類AlertViewManager,實現(xiàn)單例,用于管理項目中的所有的彈窗,可以是自定義的彈窗、原生彈窗、權(quán)限申請的彈窗
代碼建議拉到末尾下載DEMO,粘貼代碼到簡書有些空格不見了
AlertViewManager.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedefvoid(^ExecuteBlock)(void);
@interfaceAlertViewManager :NSObject
//重要提醒!:自定義的彈窗不要添加再[UIApplication sharedApplication].keyWindow上, keyWindow是會變化的,因為系統(tǒng)的彈窗是創(chuàng)建一個window,成為keyWindow,當(dāng)系統(tǒng)彈窗消失的時候,創(chuàng)建出來的window也會銷毀,但是這時候獲取的keyWindow有可能就是準(zhǔn)備銷毀這個.這樣的自定義彈窗連同keywindow一起被銷毀,導(dǎo)致信號一直是鎖著的狀態(tài),無法彈出后面的彈窗
//系統(tǒng)彈窗消失瞬間獲取到的keyWindow就是系統(tǒng)彈窗那個keyWindow,自定義彈窗添加上去會隨著keywindow銷毀而銷毀
+ (nullable instancetype)shareManager;
/**
彈窗顯示的代碼寫進BLOCK塊內(nèi)
@paramexecuteBlock <#executeBlock description#>
*/
- (void)showWithExecuteBlock:(nonnull ExecuteBlock)executeBlock;
/**
彈窗顯示完畢調(diào)用
@paramexecuteBlock 可以為空但是一定要調(diào)用
*/
- (void)dismissWithExecuteBlock:(nullable ExecuteBlock)executeBlock;
@end
AlertViewManager.m
#import "AlertViewManager.h"
//全局信號量
dispatch_semaphore_t_globalInstancesLockSEM;
//執(zhí)行QUEUE的Name
char*QUEUE_NAME_SEM ="com.alert.queueSEM";
//初始化 -- 借鑒YYWebImage的寫法
staticvoid_AlertViewInitGlobalSEM() {
staticdispatch_once_tonceToken;
dispatch_once(&onceToken, ^{
_globalInstancesLockSEM = dispatch_semaphore_create(1);
});
}
@interface AlertViewManager ()
@end
@implementationAlertViewManager
- (void)showWithExecuteBlock:(ExecuteBlock)executeBlock {
dispatch_async(dispatch_queue_create(QUEUE_NAME_SEM, DISPATCH_QUEUE_SERIAL), ^{
dispatch_semaphore_wait(_globalInstancesLockSEM, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
if(executeBlock) {
executeBlock();
}
});
});
}
- (void)dismissWithExecuteBlock:(ExecuteBlock)executeBlock {
dispatch_async(dispatch_queue_create(QUEUE_NAME_SEM, DISPATCH_QUEUE_SERIAL), ^{
dispatch_semaphore_signal(_globalInstancesLockSEM);
dispatch_async(dispatch_get_main_queue(), ^{
if(executeBlock) {
executeBlock();
}
});
});
}
+ (instancetype)shareManager {
return[[self alloc]init];
}
+ (instancetype)allocWithZone:(struct_NSZone*)zone {
staticidinstan =nil;
staticdispatch_once_tonceToken;
dispatch_once(&onceToken, ^{
instan = [superallocWithZone:zone];
});
returninstan;
}
- (instancetype)init {
if(self= [superinit]) {
_AlertViewInitGlobalSEM();
}
return self;
}
@end
使用方法
系統(tǒng)原生彈窗調(diào)用如下
[[AlertViewManager shareManager] showWithExecuteBlock:^{
[[[UIAlertView alloc] initWithTitle:@"" message:@"彈窗1" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"確定", nil] show];
}];
- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
[[AlertViewManager shareManager] dismissWithExecuteBlock:^{
}];
}
重要提醒!:
1.自定義的彈窗不要添加再[UIApplication sharedApplication].keyWindow上, keyWindow是會變化的,因為系統(tǒng)的彈窗是創(chuàng)建一個window,成為keyWindow,當(dāng)系統(tǒng)彈窗消失的時候,創(chuàng)建出來的window也會銷毀,但是這時候獲取的keyWindow有可能就是準(zhǔn)備銷毀這個.這樣的自定義彈窗連同keywindow一起被銷毀,導(dǎo)致信號一直是鎖著的狀態(tài),無法彈出后面的彈窗
2.showWithExecuteBlock和dismissWithExecuteBlock要成對出現(xiàn)。如果沒有成對出現(xiàn),信號量計數(shù)錯誤,會同時彈出多個彈窗或者不彈出
附上Demo: https://github.com/1498522607/HZSAlertViewManager
有什么問題歡迎評論留言