八. NSOperation的基本使用

一. NSOperation簡介

  1. NSOperation是在GCD之后推出的, 以操作隊(duì)列為兩大核心的多線程應(yīng)用類, 可以說他是對GCD的拓展并且進(jìn)行了一層面向?qū)ο蟮陌b

  2. 根據(jù)蘋果的解釋, NSOperation本身是一個(gè)抽象類, 它本身并沒有開啟線程, 執(zhí)行任務(wù)等能力而是使用它的兩大子類:

    • NSBlockOperation: 將操作封裝到Block中, 在線程執(zhí)行該操作時(shí), 執(zhí)行Block中的代碼
    • NSInvocationOperation: 將操作封裝到方法中, 在線程執(zhí)行操作時(shí), 執(zhí)行指定的方法
    • NSOperation也可以通過自建子類, 來執(zhí)行任務(wù), 不過由于他的兩大子類和GCD可以完成大部分任務(wù), 因此自定義的NSOperation類比較少用
  3. 通過操作隊(duì)列, 即NSOperationNSOperationQueue結(jié)合使用, 即可實(shí)現(xiàn)多線程并發(fā)執(zhí)行任務(wù)

  4. NSOperation的操作, 需要手動(dòng)開啟, 并且NSOperation的對象是single-shot objec, 即一次性的對象, 當(dāng)他啟用之后, 就不能再次使用了.

  5. 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]);
        }
      
  6. 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];
      
  7. 自定義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的使用

  1. NSOperationQueue, 即操作隊(duì)列, 與GCD的隊(duì)列有一些類似, 他可以通過和NSOperation操作組合, 達(dá)到多線程并發(fā)的效果

  2. 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ì)列
  3. 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];
        }
      
  4. 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];
        }
      
  5. 自定義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];
        }
      
  6. 操作隊(duì)列的一些使用方法

    1. 設(shè)置最大并發(fā)數(shù): queue.maxConcurrentOperationCount
      • 通過設(shè)置這個(gè)屬性, 可以控制隊(duì)列是串行還是并行
      • 如果設(shè)置為1, 那么該隊(duì)列就是串行的
      • 如果設(shè)置大于1, 那么隊(duì)列中的人物就是并發(fā)執(zhí)行的
      • 但是如果小于1, 那么該隊(duì)列就不會(huì)執(zhí)行任何任務(wù)了
    2. 暫停/恢復(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(@"+++++++++++++++++++++++++++++++++");
            }
          
  7. 操作依賴和監(jiān)聽

    1. 操作依賴
      • NSOperationQueue可以給隊(duì)列中的操作設(shè)置依賴關(guān)系
      • 如: [op addDependency:op2], 則op必須等op2執(zhí)行完畢后才能執(zhí)行
      • 這樣可以在控制任務(wù)并發(fā)執(zhí)行時(shí)的先后順序
    2. 監(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的對比

  1. 不同點(diǎn)

    • GCD是純C語言的API; 而NSOperation已經(jīng)包裝為了Object-C對象操作, 更加的面向?qū)ο?/li>
    • GCD的任務(wù)都是用Block來封裝的, 比較輕量級(jí); 而NSOperation則是直接使用對象, 比GCD較為重量級(jí)
    • 如果執(zhí)行一些簡單的線程操作, 就使用GCD
    • 如果需要控制線程中的任務(wù), NSOperation的思路比較清晰
  2. 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í)行
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容