文章目錄
一、NSOperation簡介
二、NSOperation和NSOperationQueue的基本使用
1、建立任務
2、建立隊列
3、将任務加入到隊列中
三、控制串行執行和并行執行的關鍵
四、操作依賴
五、一些其他方法
一、NSoperation的簡介
NSOperation是蘋果提供給我們的一套多線程解決方案。實際上NSOperation是基于GCD更高一層的封裝,但是比GCD更簡單易用、代碼可讀性也更高。
NSoperation 總共有兩個子類 : NSInvocationOperation 和 NSBlockOperation。
NSOperation需要配合NSOperationQueue來實作多線程。因為預設情況下,NSOperation單獨使用時系統同步執行操作,并沒有開辟新線程的能力,隻有配合NSOperationQueue才能實作異步執行。
因為NSOperation是基于GCD的,那麼使用起來也和GCD差不多,其中,NSOperation相當于GCD中的任務,而NSOperationQueue則相當于GCD中的隊列。NSOperation實作多線程的使用步驟分為三步:
1、建立任務:先将需要執行的操作封裝到一個NSOperation對象中。
2、建立隊列:建立NSOperationQueue對象。
3、将任務加入到隊列中:然後将NSOperation對象添加到NSOperationQueue中。
之後呢,系統就會自動将NSOperationQueue中的NSOperation取出來,在新線程中執行操作。
注意和GCD 類比學習 : Operation 相當于任務、操作隊列就和GCD其實是同個意思。總歸來說,NSOperation 比GCD 更有自主性!例如: 可以通過繼承重寫main方法自定義任務、可暫停任務、可恢複任務、可通過KVO監Operation是否完成或取消,可設定任務權重優先執行任務、可設定等待等
下面我們來學習下NSOperation和NSOperationQueue的基本使用。
二、NSOperation和NSOperationQueue的基本使用
NSOperation是個抽象類,并不能封裝任務。我們隻有使用它的子類來封裝任務。我們有三種方式來封裝任務:
1、使用 NSInvocationOperation
2、使用 NSBlockOperation
3、自定義子類繼承NSoperation,并重寫main方法
1、在不使用NSOperationQueue,單獨使用NSOperation的情況下系統同步執行操作,下面我們學習以下任務的三種建立方式。
1 ) 使用 NSInvocationOperation
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
// 調用start 方法執行操作
[operation start];
//其中run方法的定義如下
- (void)run
{
NSLog(@"NSInvocationOperation -> 目前線程:%@", [NSThread currentThread]);
}
運作結果 :
說明 :從中可以看到,在沒有使用隊列(NSOperationQueue),單獨使用NSInvocationOperation的情況下,NSInvocationOperation在主線程執行操作,并沒有開啟新線程。
.
.
.
2 ) 使用 NSBlockOperation
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"NSBlockOperation -> 目前線程:%@", [NSThread currentThread]);
}];
// NSBlockOperation 提供 addExecutionBlock,可通過這個進行添加額外的操作,這些額外操作在有可能在主線程中執行,也有可能會開啟子線程去執行。
[blockOperation addExecutionBlock:^{
NSLog(@"NSBlockOperation 額外操作任務1:%@", [NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"NSBlockOperation 額外操作任務2:%@", [NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"NSBlockOperation 額外操作任務3:%@", [NSThread currentThread]);
}];
[blockOperation start];
運作結果 :
說明 :
<1> 在沒有使用NSOperationQueue、單獨使用NSBlockOperation的情況下,NSBlockOperation也是在主線程執行操作,并沒有開啟新線程
<2> NSBlockOperation 提供 addExecutionBlock,可通過這個進行添加額外的操作,這些額外操作在有可能在主線程中執行,也有可能會開啟子線程去執行
.
.
.
3 ) 自定義子類繼承NSoperation,并重寫main方法
先定義一個繼承自NSOperation的子類,重寫main方法
CustomOperation.h
#import <Foundation/Foundation.h>
@interface CustomOperation : NSOperation
@end
CustomOperation.m
#import "CustomOperation.h"
@implementation CustomOperation
- (void)main
{
for (int i = ; i < ; i++) {
NSLog(@"定義繼承自NSOperation的子類封裝任務:%@",[NSThread currentThread]);
}
}
@end
導入頭檔案 CustomOperation.h
CustomOperation *customOperation = [[CustomOperation alloc]init];
[customOperation start];
運作結果 :
說明 : 在沒有使用NSOperationQueue、單獨使用自定義子類的情況下,是在主線程執行操作,并沒有開啟新線程。
2、建立隊列
和GCD中的并發隊列、串行隊列略有不同的是:NSOperationQueue一共有兩種隊列:主隊列、其他隊列。其中其他隊列同時包含了串行、并發功能。下邊是主隊列、其他隊列的基本建立方法和特點。
主隊列
凡是添加到主隊列的任務,必然是放在主線程中執行
其它隊列
添加到這種隊列中的任務(NSOperation),就會自動放到子線程中執行,同時包含了:串行、并發功能
3、将任務添加到隊列中去
// 1、 建立 隊列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
// 2、定義任務
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = ; i < ; i++) {
NSLog(@"NSBlockOperation任務:%@",[NSThread currentThread]);
}
}];
// 3、 将任務添加到隊列中
[queue addOperation:invocationOperation];
[queue addOperation:blockOperation];
運作結果 :
說明 :NSInvocationOperation和NSOperationQueue結合後能夠開啟新線程,進行并發執行NSBlockOperation和NSOperationQueue也能夠開啟新線程,進行并發執行。
當然,你也可以無需先建立任務,可以在block中添加任務,直接将block中的人添加到隊列中。
[queue addOperationWithBlock:^{
for (int i = ; i < ; i++) {
NSLog(@"任務:%@",[NSThread currentThread]);
}
}];
運作結果 :
.
.
.
三、控制串行執行和并行執行的關鍵
之前我們說過,NSOperationQueue建立的其他隊列同時具有串行、并發功能,上邊我們示範了并發功能,那麼他的串行功能是如何實作的?
這裡有個關鍵參數maxConcurrentOperationCount,這個官方的解釋是如下
The maximum number of queued operations that can execute at the same time.
中文可譯為 : 可以同時執行的隊列操作的最大數量
maxConcurrentOperationCount 預設值為-1,表示不進行限制,預設為并發執行
當 maxConcurrentOperationCount = 1,串行執行任務
當maxConcurrentOperationCount大于1時,進行并發執行,當然這個值不應超過系統限制,即使自己設定一個很大的值,系統也會自動調整。
注意 : 這個參數主要控制隊列是串行執行任務還是并行執行任務,具體開啟的線程數交由系統控制。
具體看下面一個例子 :
// 建立隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//queue.maxConcurrentOperationCount = -1;
queue.maxConcurrentOperationCount = ;
// 添加操作
[queue addOperationWithBlock:^{
NSLog(@"1-----%@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"2-----%@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"3-----%@", [NSThread currentThread]);
}];
當 maxConcurrentOperationCount = -1 時控制台輸出
當 maxConcurrentOperationCount = 1 時控制台輸出
可以看出:當最大并發數為1時,任務是按順序串行執行的。當最大并發數為2時,任務是并發執行的。而且開啟線程數量是由系統決定的,不需要我們來管理。這樣看來,是不是比GCD還要簡單了許多?
四、設定操作依賴
NSOperation和NSOperationQueue最吸引人的地方是它能添加操作之間的依賴關系。比如說有A、B兩個操作,其中A執行完操作,B才能執行操作,那麼就需要讓B依賴于A。具體如下:
- (void)addOperationDepedency
{
// 1、 建立隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2、 建立任務
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1-----%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2-----%@", [NSThread currentThread]);
}];
//讓op2 依賴于 op1,則先執行op1,在執行op2
[op2 addDependency:op1];
// 3、 将任務添加到隊列中
[queue addOperation:op2];
[queue addOperation:op1];
}//添加操作依賴
運作結果 :
.
.
.
五、一些其他方法
NSOperation提供的方法,可取消單個操作
- (void)cancel;
NSOperationQueue提供的方法,可以取消隊列的所有操作
- (void)cancelAllOperations;
可設定任務的暫停和恢複,YES代表暫停隊列,NO代表恢複隊列
- (void)setSuspended:(BOOL)b;
判斷暫停狀态
- (BOOL)isSuspended;
注意:
這裡的暫停和取消并不代表可以将目前的操作立即取消,而是當目前的操作執行完畢之後不再執行新的操作。
暫停和取消的差別就在于:暫停操作之後還可以恢複操作,繼續向下執行;而取消操作之後,所有的操作就清空了,無法再接着執行剩下的操作。
相關連結 :
本文Demo傳送門
GCD和NSOperation的差別和選用