利用操作隊列和信號量來實現(xiàn)操作同步
最近在開發(fā)iOS過程中遇到一個問題:某一些操作需要在一個初始化操作后才允許執(zhí)行。但是這些操作的執(zhí)行時刻有可能比初始化操作來得要快。那么,如果不等待初始化操作后再執(zhí)行的話,這些操作就等于是丟失了。
針對這個問題,我想到了兩種解決方案:***就是執(zhí)行這些操作之前先判斷是否已經初始化,如果尚未初始化則使用一個數(shù)組隊列把操作參數(shù)及調用的方法存儲起來,等待初始化完成后再檢測數(shù)組隊列中的保存的操作進行調用并清空隊列。但這種方式有個問題就是操作中傳遞的參數(shù)以及調用方法引用都需要自己來維護,這無疑是給自己帶來了一定的工作量以及風險,稍有不慎就有可能會導致內存泄露。
因此第二中解決方法就是利用串行隊列結合信號量的方式來控制操作的執(zhí)行。此方案的思路是,先創(chuàng)建一條串行隊列,此隊列用于執(zhí)行所有的操作。但是***入隊的是一個等待信號的操作。而這個信號的初始值是0,直到初始化操作完成后才會發(fā)送一個信號來通知此操作。因此,在尚未初始化完成的時候此隊列是一直處于阻塞狀態(tài)的。所以到有操作進入隊列時都會立刻執(zhí)行,而是需要等到初始化信號過來后才開始執(zhí)行。
為了驗證這一想法,我新建了一個應用工程,在ViewController中定義了操作隊列_quque和信號量_sema,如下:
- @interface ViewController : UIViewController
- {
- @private
- dispatch_queue_t _queue;
- dispatch_semaphore_t _sema;
- }
- @end
初始化時創(chuàng)建操作隊列
- - (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為執(zhí)行的操作。Signal為通知阻塞隊列可以執(zhí)行操作了。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都是不會有l(wèi)og信息顯示,直到點擊Signal后,之前點擊的DoSomething將會一一打印出來信息。
可見這種解決方案是可行的,并且可以更加容易操作。