一. NSOperation簡介
NSOperation是在GCD之后推出的, 以操作和隊(duì)列為兩大核心的多線程應(yīng)用類, 可以說他是對GCD的拓展并且進(jìn)行了一層面向?qū)ο蟮陌b
-
根據(jù)蘋果的解釋, NSOperation本身是一個(gè)抽象類, 它本身并沒有開啟線程, 執(zhí)行任務(wù)等能力而是使用它的兩大子類:
- NSBlockOperation: 將操作封裝到Block中, 在線程執(zhí)行該操作時(shí), 執(zhí)行Block中的代碼
- NSInvocationOperation: 將操作封裝到方法中, 在線程執(zhí)行操作時(shí), 執(zhí)行指定的方法
- NSOperation也可以通過自建子類, 來執(zhí)行任務(wù), 不過由于他的兩大子類和GCD可以完成大部分任務(wù), 因此自定義的NSOperation類比較少用
通過操作和隊(duì)列, 即NSOperation和NSOperationQueue結(jié)合使用, 即可實(shí)現(xiàn)多線程并發(fā)執(zhí)行任務(wù)
NSOperation的操作, 需要手動(dòng)開啟, 并且NSOperation的對象是single-shot objec, 即一次性的對象, 當(dāng)他啟用之后, 就不能再次使用了.
-
NSInvocationOperation的簡單使用
創(chuàng)建一個(gè)NSInvocationOperation對象, 并且在初始化的時(shí)候, 指定該操作要調(diào)用的方法
實(shí)現(xiàn)調(diào)用的方法
-
開啟操作
- (void)invocation { NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil]; [op1 start]; } - (void)download { // 任務(wù)默認(rèn)是在主線程中執(zhí)行的 NSLog(@"download---%@", [NSThread currentThread]); }
-
NSBlockOperation的簡單使用
創(chuàng)建NSBlockOperation對象, 并且在創(chuàng)建的同時(shí), 要將操作封裝到block的內(nèi)部
當(dāng)操作開始執(zhí)行的時(shí)候, 系統(tǒng)就會(huì)調(diào)用block內(nèi)封裝的代碼
NSBlockOperation對象, 在開啟前可以追加任務(wù)
當(dāng)操作中的任務(wù)數(shù)量大于一的時(shí)候, NSBlockOperation就會(huì)開啟子線程來執(zhí)行任務(wù)
-
開啟操作
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"op2 --- %@", [NSThread currentThread]); }]; // 2. 追加任務(wù),當(dāng)一個(gè)操作中的任務(wù)數(shù)量大于1的時(shí)候,就會(huì)開啟子線程執(zhí)行任務(wù) [op2 addExecutionBlock:^{ NSLog(@"op2Add --- %@", [NSThread currentThread]); }]; [op2 addExecutionBlock:^{ NSLog(@"op2ADD --- %@", [NSThread currentThread]); }]; // 3. 開啟任務(wù) [op2 start];
-
自定義NSOperation子類
自定義的NSOperation子類, 需要?jiǎng)?chuàng)建一個(gè)繼承自NSOperation的類
在類中, 實(shí)現(xiàn)
-main方法, 該方法中的代碼, 就是操作開啟時(shí)要執(zhí)行的代碼-
開啟操作
#import "MYOperation.h" @implementation MYOperation - (void)main { NSLog(@"main --- %@", [NSThread currentThread]); } @end // 3. 自建Operation類 - (void)myOP { MYOperation *op = [[MYOperation alloc] init]; [op start]; }
二. NSOperationQueue的使用
NSOperationQueue, 即操作隊(duì)列, 與GCD的隊(duì)列有一些類似, 他可以通過和NSOperation操作組合, 達(dá)到多線程并發(fā)的效果
-
NSOperationQueue的兩種隊(duì)列
- 主隊(duì)列:
[NSOpedationQueue mainQueue], 凡是在主隊(duì)列中執(zhí)行的操作, 都在主線程執(zhí)行 - 非主隊(duì)列: 直接通過
[[NSoperationQueue alloc] init]獲得的隊(duì)列, 非主隊(duì)列同時(shí)具備了并發(fā)和串行的功能 - 當(dāng)前隊(duì)列:
[NSOperationQueue currentQueue], 這個(gè)方法可以獲得, 調(diào)用方法時(shí)所在的隊(duì)列, 但并不是創(chuàng)建隊(duì)列
- 主隊(duì)列:
-
NSInvocationOperation使用隊(duì)列
主隊(duì)列的任務(wù)都在主線程執(zhí)行, 因此不做討論
非主隊(duì)列中的任務(wù)分為串行和并發(fā), 默認(rèn)是并發(fā)執(zhí)行的
先創(chuàng)建隊(duì)列, 然后將任務(wù)添加到隊(duì)列中去
-
注意: 將任務(wù)添加到隊(duì)列中, 不必手動(dòng)開啟任務(wù), 進(jìn)入隊(duì)列的任務(wù)會(huì)自動(dòng)執(zhí)行
- (void)invocation { // 1. 封裝操作 NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil]; NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil]; NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil]; // 2. 創(chuàng)建操作隊(duì)列 // NSOperationQueue *queue = [NSOperationQueue mainQueue]; // 主隊(duì)列,串行隊(duì)列 // NSOperationQueue *queue = [NSOperationQueue currentQueue]; // 當(dāng)前隊(duì)列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 并發(fā)隊(duì)列 // 3. 將操作添加到隊(duì)列中 [queue addOperation:op]; [queue addOperation:op2]; [queue addOperation:op3]; }
-
NSBlockOperation使用隊(duì)列
與上述InvocationOperation的使用方法大致相同
創(chuàng)建操作隊(duì)列, 為了讓任務(wù)并發(fā)執(zhí)行, 使用非主隊(duì)列
將任務(wù)添加到隊(duì)列中
-
可以給blockOperation追加任務(wù), 追加的任務(wù)同樣會(huì)在子線程中執(zhí)行
- (void)block { // 1. 封裝操作 NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"op ---- %@", [NSThread currentThread]); }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"op2 ---- %@", [NSThread currentThread]); }]; // 2. 創(chuàng)建操作隊(duì)列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // NSOperationQueue *queue = [NSOperationQueue mainQueue]; // 3. 追加任務(wù),追加的任務(wù)一定會(huì)在子線程中執(zhí)行 [op addExecutionBlock:^{ NSLog(@"opADD ---- %@", [NSThread currentThread]); }]; // 4. 將操作添加到隊(duì)列中 [queue addOperation:op]; [queue addOperation:op2]; }
-
自定義NSOperation子類使用隊(duì)列
自定義的NSOperation子類, 需要實(shí)現(xiàn)main方法, 來規(guī)定該操作執(zhí)行的任務(wù)
-
其他使用步驟與上述的方法相同
- (void)myOp { MYOperation *op = [[MYOperation alloc] init]; MYOperation *op2 = [[MYOperation alloc] init]; MYOperation *op3 = [[MYOperation alloc] init]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:op]; [queue addOperation:op2]; [queue addOperation:op3]; }
-
操作隊(duì)列的一些使用方法
- 設(shè)置最大并發(fā)數(shù):
queue.maxConcurrentOperationCount- 通過設(shè)置這個(gè)屬性, 可以控制隊(duì)列是串行還是并行
- 如果設(shè)置為1, 那么該隊(duì)列就是串行的
- 如果設(shè)置大于1, 那么隊(duì)列中的人物就是并發(fā)執(zhí)行的
- 但是如果小于1, 那么該隊(duì)列就不會(huì)執(zhí)行任何任務(wù)了
- 暫停/恢復(fù)/取消隊(duì)列中的任務(wù)
- 暫停/恢復(fù):
queue.suspended- 該屬性接收BOOL值, 如果傳入YES, 隊(duì)列當(dāng)前就會(huì)暫停執(zhí)行任務(wù)
- 如果傳入NO, 隊(duì)列就會(huì)繼續(xù)執(zhí)行任務(wù), 因此暫停操作是可以恢復(fù)的
- 注意: 如果臨時(shí)設(shè)置為暫停的話, 那么隊(duì)列會(huì)處理完當(dāng)前任務(wù), 暫時(shí)不執(zhí)行下個(gè)任務(wù)
- 取消任務(wù):
[queue cancelAllOperations]該方法會(huì)取消隊(duì)列中的所有任務(wù)
注意: 取消任務(wù), 也是需要等當(dāng)前任務(wù)執(zhí)行結(jié)束之后, 后面的任務(wù)都取消執(zhí)行
取消操作是不可恢復(fù)的
-
蘋果官方建議, 每當(dāng)執(zhí)行一次耗時(shí)操作后, 就手動(dòng)檢查一下當(dāng)前隊(duì)列是否為取消狀態(tài), 如果是, 就直接return, 有利于提高性能
-(void)main { //耗時(shí)操作1 for (int i = 0; i<1000; i++) { NSLog(@"任務(wù)1-%d--%@",i,[NSThread currentThread]); } NSLog(@"+++++++++++++++++++++++++++++++++"); //蘋果官方建議,每當(dāng)執(zhí)行完一次耗時(shí)操作之后,就查看一下當(dāng)前隊(duì)列是否為取消狀態(tài),如果是,那么就直接退出 //好處是可以提高程序的性能 if (self.isCancelled) { return; } //耗時(shí)操作2 for (int i = 0; i<1000; i++) { NSLog(@"任務(wù)1-%d--%@",i,[NSThread currentThread]); } NSLog(@"+++++++++++++++++++++++++++++++++"); }
- 暫停/恢復(fù):
- 設(shè)置最大并發(fā)數(shù):
-
操作依賴和監(jiān)聽
- 操作依賴
- NSOperationQueue可以給隊(duì)列中的操作設(shè)置依賴關(guān)系
- 如:
[op addDependency:op2], 則op必須等op2執(zhí)行完畢后才能執(zhí)行 - 這樣可以在控制任務(wù)并發(fā)執(zhí)行時(shí)的先后順序
- 監(jiān)聽:
op.completionBlock給某一個(gè)操作設(shè)置監(jiān)聽
當(dāng)監(jiān)聽到這個(gè)操作執(zhí)行完畢的時(shí)候, 就會(huì)調(diào)用block中的代碼
-
通過與操作依賴連用, 可以監(jiān)聽隊(duì)列中所有的任務(wù)是否完成
- (void)invocation { NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"op ---- %@", [NSThread currentThread]); }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"op2 ---- %@", [NSThread currentThread]); }]; NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"op3 ---- %@", [NSThread currentThread]); }]; // 設(shè)置監(jiān)聽,該任務(wù)會(huì)在所有任務(wù)都完成之后調(diào)用 op3.completionBlock = ^{ NSLog(@"操作已經(jīng)完成%@", [NSThread currentThread]); }; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 設(shè)置依賴(優(yōu)先級(jí)),op < op2 [op addDependency:op2]; [op2 addDependency:op3]; [queue addOperation:op]; [queue addOperation:op2]; [queue addOperation:op3]; }
- 操作依賴
三. GCD和NSOperation的對比
-
不同點(diǎn)
- GCD是純C語言的API; 而NSOperation已經(jīng)包裝為了Object-C對象操作, 更加的面向?qū)ο?/li>
- GCD的任務(wù)都是用Block來封裝的, 比較輕量級(jí); 而NSOperation則是直接使用對象, 比GCD較為重量級(jí)
- 如果執(zhí)行一些簡單的線程操作, 就使用GCD
- 如果需要控制線程中的任務(wù), NSOperation的思路比較清晰
-
NSOperation的優(yōu)點(diǎn)
- NSOperationQueue, 擁有Cancel和Suspend這樣的方法來取消/暫停任務(wù); 而GCD的任務(wù)是無法取消的, 運(yùn)行之后就無法控制了
- NSOperation可以方便指定的操作間設(shè)置依賴關(guān)系, 控制任務(wù)的執(zhí)行順序
- NSOperation可以通過KVO來對NSOperation對象進(jìn)行控制(如監(jiān)聽操作是否被取消或者完成)
- NSOperation可以指定操作優(yōu)先級(jí), 優(yōu)先級(jí)高的先執(zhí)行