針對這個問題,我想到了兩種解決方案:第一就是執行這些操作之前先判斷是否已經初始化,如果尚未初始化則使用一個數組隊列把操作參數及調用的方法存儲起來,等待初始化完成後再檢測數組隊列中的保存的操作進行調用並清空隊列。但這種方式有個問題就是操作中傳遞的參數以及調用方法引用都需要自己來維護,這無疑是給自己帶來了一定的工作量以及風險,稍有不慎就有可能會導致內存洩露。
因此第二中解決方法就是利用串行隊列結合信號量的方式來控制操作的執行。此方案的思路是,先創建一條串行隊列,此隊列用於執行所有的操作。但是最先入隊的是一個等待信號的操作。而這個信號的初始值是0,直到初始化操作完成後才會發送一個信號來通知此操作。因此,在尚未初始化完成的時候此隊列是一直處於阻塞狀態的。所以到有操作進入隊列時都會立刻執行,而是需要等到初始化信號過來後才開始執行。
為了驗證這一想法,我新建了一個應用工程,在ViewController中定義了操作隊列_quque和信號量_sema,如下:
@interface ViewController : UIViewController
{
@private
dispatch_queue_t _queue;
dispatch_semaphore_t _sema;
}
@end
初始化時創建操作隊列
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])
{
_queue = dispatch_queue_create("cn.vimfung.demo", DISPATCH_QUEUE_SERIAL);
}
return self;
}
在ViewController中定義了三個按鈕,分別為DoSomething、Signal、Wait。其中DoSomething為執行的操作。Signal為通知阻塞隊列可以執行操作了。Wait為阻塞當前隊列。
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn setTitle:@"DoSomething" forState:UIControlStateNormal];
[btn sizeToFit];
[btn addTarget:self action:@selector(doSomethingHandler:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn1 setTitle:@"Signal" forState:UIControlStateNormal];
[btn1 sizeToFit];
[btn1 addTarget:self action:@selector(signalHanlder:) forControlEvents:UIControlEventTouchUpInside];
btn1.frame = CGRectMake(0.0, 50.0, btn1.frame.size.width, btn1.frame.size.height);
[self.view addSubview:btn1];
UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn2 setTitle:@"Wait" forState:UIControlStateNormal];
[btn2 sizeToFit];
[btn2 addTarget:self action:@selector(waitHanlder:) forControlEvents:UIControlEventTouchUpInside];
btn2.frame = CGRectMake(0.0, 100.0, btn2.frame.size.width, btn2.frame.size.height);
[self.view addSubview:btn2];
}
- (void)doSomethingHandler:(id)sender
{
dispatch_async(_queue, ^{
NSLog(@"do something");
});
}
- (void)signalHanlder:(id)sender
{
dispatch_semaphore_signal(_sema);
}
- (void)waitHanlder:(id)sender
{
if (_sema)
{
dispatch_release(_sema);
}
_sema = dispatch_semaphore_create(0);
dispatch_async(_queue, ^{
dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER);
});
}
運行後,先點擊Wait讓隊列阻塞、然後這時無論怎麼點擊DoSomething都是不會有log信息顯示,直到點擊Signal後,之前點擊的DoSomething將會一一打印出來信息。
可見這種解決方案是可行的,並且可以更加容易操作。