NSOpertation是一套OC的API,是對GCD進行的Cocoa抽象。
NSOperation有兩種不同類型的隊列,主隊列和自定義隊列。
主隊列運行于主線程上,自定義隊列在后臺運行。
【NSBlockOperation】
通過Block創建任務,下面比較主隊列和自定義隊列的區別:
將自定義隊列聲明為成員變量,并進行初始化:
@property (nonatomic, strong) NSOperationQueue *myqueue;
self.myqueue = [[NSOperationQueue alloc] init];
獲取主隊列的方法為[NSOperationQueue mainQueue]。
隊列有一個方法addOperationWithBlock方法用于添加一個用Block描述的任務。
具體代碼為:
- (void)NSBlockOperation{// 自定義隊列在子線程中運行。[self.myqueue addOperationWithBlock:^{NSLog(@"%@",[NSThread currentThread]);}];// 主隊列任務在主線程中運行。[[NSOperationQueue mainQueue] addOperationWithBlock:^{NSLog(@"%@",[NSThread currentThread]);}];}
執行這個方法,得到的結果如下,可見與上面的描述相符。 2015-02-17 10:46:15.308 NSOpertaion[709:17138] <NSThread: 0x7b9aa440>{number = 2, name = (null)}
2015-02-17 10:46:15.319 NSOpertaion[709:17067] <NSThread: 0x7b978550>{number = 1, name = main}
需要定義一個回調方法,好處是可以接收一個id類型的object作為消息。
例如:
- (void)NSInvocationOperation{NSDictionary *msg = @{@"name" : @"op",@"message" : @"hello"};NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(InvocationCall:) object:msg];[self.myqueue addOperation:op];}
實現回調方法:
- (void)InvocationCall:(id)obj{NSLog(@"%@ with object %@",[NSThread currentThread],obj);
}
調用后,打印的結果如下,可以看到object對象被傳了過來。
2015-02-17 10:56:03.764 NSOpertaion[812:26537] <NSThread: 0x7ba6eb10>{number = 2, name = (null)} with object {message = hello;name = op;
}
【任務執行順序】
在默認情況下,自定義隊列是并行隊列,執行無序;而主隊列為串行隊列,有序執行。下面進行實驗驗證說法:
// 自定義隊列在子線程中運行。for (int i = 0; i < 9; i++) {[self.myqueue addOperationWithBlock:^{NSLog(@"%@ with no %d",[NSThread currentThread],i);}];}// 主隊列任務在主線程中運行。for (int i = 0; i < 9; i++) {[[NSOperationQueue mainQueue] addOperationWithBlock:^{NSLog(@"%@ with no %d",[NSThread currentThread],i);}];}
執行結果如下,可見與上面的描述相同。
2015-02-17 11:04:08.421 NSOpertaion[919:32337] <NSThread: 0x7bf91f50>{number = 9, name = (null)} with no 6
2015-02-17 11:04:08.421 NSOpertaion[919:32331] <NSThread: 0x7bf91c10>{number = 4, name = (null)} with no 1
2015-02-17 11:04:08.421 NSOpertaion[919:32338] <NSThread: 0x7bf915c0>{number = 6, name = (null)} with no 5
2015-02-17 11:04:08.421 NSOpertaion[919:32336] <NSThread: 0x7dab62c0>{number = 7, name = (null)} with no 4
2015-02-17 11:04:08.421 NSOpertaion[919:32339] <NSThread: 0x7dab61b0>{number = 3, name = (null)} with no 7
2015-02-17 11:04:08.421 NSOpertaion[919:32330] <NSThread: 0x7dab6110>{number = 2, name = (null)} with no 0
2015-02-17 11:04:08.421 NSOpertaion[919:32341] <NSThread: 0x7d97b3f0>{number = 5, name = (null)} with no 8
2015-02-17 11:04:08.421 NSOpertaion[919:32328] <NSThread: 0x7be6d450>{number = 10, name = (null)} with no 3
2015-02-17 11:04:08.421 NSOpertaion[919:32329] <NSThread: 0x7dab6450>{number = 8, name = (null)} with no 2
2015-02-17 11:04:08.448 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 0
2015-02-17 11:04:08.449 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 1
2015-02-17 11:04:08.449 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 2
2015-02-17 11:04:08.449 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 3
2015-02-17 11:04:08.450 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 4
2015-02-17 11:04:08.450 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 5
2015-02-17 11:04:08.450 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 6
2015-02-17 11:04:08.450 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 7
2015-02-17 11:04:08.450 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 8
【自定義隊列順序執行】
使用NSBlockOperation對象的addDependency設置依賴關系,只有依賴的對象執行完畢后,自己才能執行。
例如下面的例子,三個任務要順序執行,先下載,再處理,最后顯示,通過這樣的設定可以保證順序:
- (void)SerialOperation{NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"下載");}];NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"處理");}];NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"顯示");}];[op2 addDependency:op1];[op3 addDependency:op2];[self.myqueue addOperation:op1];[self.myqueue addOperation:op2];[self.myqueue addOperation:op3];}
注意這個執行和串行隊列的異步任務不同點是,串行隊列的異步任務僅僅開一個線程;自定義隊列的順序執行可能開辟多個但不會太多個線程。 注意上面的代碼有一定的問題,因為顯示只有主線程可以處理,所以op3應該放入主線程。
Tip:依賴關系可以跨隊列,因此op3依賴op2在主線程中仍然有效,只需要修改op3的入隊代碼為:
[[NSOperationQueue mainQueue] addOperation:op3];
Tip:注意避開循環依賴,程序會崩潰。
【設定多線程的最大開銷】
設定同時執行的最大線程數:通過隊列的setMaxConcurrentOperationCount方法來設定,例如:
[self.myqueue setMaxConcurrentOperationCount:3];
應用場景:網絡通信,例如3G開3個子線程,WIFI開6個子線程。
Tip:線程的開銷主要是CPU和內存,還會耗電,因此應該考慮軟件的能耗。
Tip:AFNetworing的底層是使用GCD開發的,接口是NSOperation。