最近在開發iOS過程中遇到一個問題:某一些操作需要在一個初始化操作後才允許執行。但是這些操作的執行時刻有可能比初始化操作來得要快。那麼,如果不等待初始化操作後再執行的話,這些操作就等於是丟失了。
針對這個問題,我想到了兩種解決方案:第一就是執行這些操作之前先判斷是否已經初始化,如果尚未初始化則使用一個數組隊列把巨集指令引數及調用的方法儲存起來,等待初始化完成後再檢測數組隊列中的儲存的操作進行調用並清空隊列。但這種方式有個問題就是操作中傳遞的參數以及調用方法引用都需要自己來維護,這無疑是給自己帶來了一定的工作量以及風險,稍有不慎就有可能會導致記憶體泄露。
因此第二中解決方案就是利用串列隊列結合訊號量的方式來控制操作的執行。此方案的思路是,先建立一條串列隊列,此隊列用於執行所有的操作。但是最先入隊的是一個等待訊號的操作。而這個訊號的初始值是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將會一一列印出來資訊。
可見這種解決方案是可行的,並且可以更加容易操作。