iOS 多線程系列 -- NSOperation

iOS 多線程系列 -- 基礎概述
iOS 多線程系列 -- pthread
iOS 多線程系列 -- NSThread
iOS 多線程系列 -- GCD全解一(基礎)
iOS 多線程系列 -- GCD全解二(常用方法)
iOS 多線程系列 -- GCD全解三(進階)
iOS 多線程系列 -- NSOperation
測試Demo的GitHub地址

上一篇我們介紹過GCD,這一篇我們學習一下iOS另一種多線程技術--NSOperation技術.

NSOperation也是蘋果提供給我們的一套多線程解決方案。實際上它也是基于GCD的一層OC封裝,在擁有GCD很多優點的同時,比GCD擁有更強的可控性和代碼可讀性.和GCD
一樣,NSOperation一樣不需要我們管理線程的創建銷毀等事情,系統會自動根據CPU負載情況,創建合適的線程去執行任務;但同時, NSOperation提供了更多GCD不提供的控制線程屬性,例如我們可以控制最大并發數量、取消隊列中還沒有執行的任務(注意是還沒有執行的任務)等

NSOperation

1.基礎知識

1.1 認識NSOperation類

  • NSOperation除了代表iOS多線程的一種技術,還是這個技術中的一個重要類。
  • NSOperation本身是抽象基類,不能直接使用,但是他封裝了需要執行的操作和執行操作所需的數據方法等。在NSOperation基礎上,系統提供了兩個子類NSBlockOperation和NSInvocationOperation供我們具體使用,當然,我們也可以自己封裝自定義的NSOperation。

1.2 NSOperation的常用方法、屬性介紹

  • setCompletionBlock 監聽NSOperation的完成,調用NSOperation的setCompletionBlock方法來設置操作完成后想做的事情
    • completionBlock 會在后臺線程執行,不會再主線程執行,所以如果需要更新UI,需要注意線程
    • completionBlock 不能接受參數,沒有返回值,因此在使用的時候,會有很多限制
  • cancel NSOperation實例調用cancel方法,取消operation執行,但并不是絕對取消任務,如果operation加入到Queue中或者operation已經start了,則無法取消成功。同時還會設置isCancelled屬性為YES,這個屬性在自定義NSOperation的時候很重要
1)- (id)init;//NSOperation實例初始化方法,用于創建一個NSOperation實例。
2)- (void)start;//該方法是operation的起點,如果需要創建并發operation必須,覆蓋start方法。同時調用start方法,會默認做各種驗證。
3)- (BOOL)isCancelled;//該property,是用于標示某個operation是否cancel。對于多線程來說需要不斷檢測這個值。
4)- (void)cancel;//調用cancel方法會取消一個operation,但是如果operation加入到Queue中或者operation已經start了,則無法取消成功,調用cancel也不一定立即執行cancel操作,需要等待時間周期。
5)- (BOOL)isExecuting;//判定operation是否正在執行。
6)- (BOOL)isFinished;//判定operation是否完成,cancel掉某個operation,也會將該operation的該字段設置成為YES。
7)- (BOOL)isConcurrent;//判定該線程是否是并發線程,即調用該operation的start方法的線程是否與operation所在線程相同。
8)- (BOOL)isReady;//在start方法開始之前,需要確定operation是否ready,默認為YES,如果該operation沒有ready,則不會start。
9)- (void)addDependency:(NSOperation *)op;//該方法用于配置operation之間的依賴關系,涉及執行順序稍后會介紹。如果不是手動調用start去執行operation,一定要在將其加入到Queue之前做好依賴,因為一旦加入到Queue中,其也許很快會執行,依賴關系將不會起作用。
10)- (void)removeDependency:(NSOperation *)op;//相對應add,其為移除兩個operation之間的依賴關系。
11)- (NSArray *)dependencies;//獲取operation的依賴關系的數組。
12)- (NSOperationQueuePriority)queuePriority;//如果將operation加入到Queue中,設定其在Queue中的優先級,優先級高的先執行的概率大,但并不代表一定會先執行,執行順序稍后介紹。
13)- (void)setQueuePriority:(NSOperationQueuePriority)p;//setter方法。
14)- (void (^)(void))completionBlock NS_AVAILABLE(10_6, 4_0);//在operation完成之后會調用completionBlock,你可以自定義執行行為。
15)- (void)waitUntilFinished NS_AVAILABLE(10_6, 4_0);//設定是否等待operation執行結束,如果為YES,該線程會一直等待operation執行結束,才會執行接下來的代碼。
16)- (double)threadPriority NS_AVAILABLE(10_6, 4_0);//設定operation的線程優先級,涉及執行順序稍后介紹。線程優先級默認為0.5,最低為0,最大為1.即使設定了線程優先級,也只能保證其在main方法范圍內有效,operation的其他代碼仍然執行在默認線程。

2.兩個系統提供的NSOperation子類

2.1 NSBlockOperation

  • 能夠并發地執行一個或多個block對象,所有相關的block都執行完之后,操作才算完成

  • 創建NSBlockOperation對象

    + (id)blockOperationWithBlock:(void (^)(void))block;
    
  • 通過addExecutionBlock:方法添加更多的操作

    - (void)addExecutionBlock:(void (^)(void))block;
    
  • 注意:

    • 調用start方法,默認會在調用start的線程中執行任務,但是只要NSBlockOperation封裝的操作數大于1,調用start方法執行任務,就會異步執行操作;可以在下面示例代碼中驗證這一點
  • 示例代碼:

NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ // 在主線程
        NSLog(@"下載1------%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{ // 添加額外的任務(在子線程執行)
        NSLog(@"下載2------%@", [NSThread currentThread]);
    }];
    [op start];//開始執行任務
  • 示例代碼打印結果:

2017-07-04 20:01:09.586 Test - 多線程[20794:4540810] 下載1------<NSThread: 0x60000007fb80>{number = 1, name = main}
2017-07-04 20:01:09.586 Test - 多線程[20794:4540931] 下載2------<NSThread: 0x608000269e00>{number = 3, name = (null)}

2.2 NSInvocationOperation

  • 基于一個對象和selector來創建操作。如果你已經有現有的方法來執行需要的任務,就可以使用這個類
  • 創建NSInvocationOperation對象

    NSInvocationOperation *invo=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

  • 線程啟動的兩種方法(start和NSOperationQueue):
    • 調用start方法開始執行操作

      - (void)start;
      一旦執行操作,就會調用target的sel方法
      
      //可以調用這個方法啟動執行任務,但是這樣的方法不會啟動新的線程,只會在當前線程執行
      [invo start];
      
  • 注意
    • 默認情況下,調用了start方法后并不會開一條新線程去執行操作,而是在當前線程同步執行操作

3.進階 -- 自定義operation

如果NSInvocationOperation和NSBlockOperation對象不能滿足需求, 你可以直接繼承NSOperation, 并添加任何你想要的行為。

NOTE 需要自己創建自動釋放池(因為如果是異步操作,子線程運行循環默認不工作,不會主動創建自動釋放池),但是在查看最新的蘋果官方文檔時發現,里面指出 “This method will automatically execute within an autorelease pool provided by NSOperation, so you do not need to create your own autorelease pool block in your implementation” 也就是說不需要我們創建autorelease pool。

  • 優點:提供了更加靈活可控的處理方式,如:
    • 我們在自定義任務的時候,可以非常方便的在需要的地方監聽NSOperation的isCanceled屬性并作出我們需要的處理。

3.1 創建非并發的NSOperation

  • 只需要重載-(void)main這個方法,在這個方法里面執行主任務
  • 在重載main方法里面,添加自動釋放池,因為子線程運行循環默認不工作,不會主動創建自動釋放池
  • 定期監聽operation的isCanceled屬性并正確的響應cancel操作即可.下面列舉一般在什么地方添加監聽:
    • 在開始執行前判斷isCancelled,如果為YES直接return中斷任務
    • 執行任何實際的工作之前
    • 在循環的每次迭代過程中,如果每個迭代相對較長可能需要調用多次
    • 代碼中相對比較容易中止操作的任何地方
  • 示例代碼如下:
//捕獲異常
@try {
    //在這里我們要創建自己的釋放池,因為子線程運行循環默認不工作,不會主動創建自動釋放池
    @autoreleasepool {
        if (self.isCancelled)
        {
            NSLog(@"0000self.isCancelled = yes , return");
            return;
        }
        for (NSInteger i = 0; i<100; i++) {
            [NSThread sleepForTimeInterval:0.1];
            NSString *string = [NSString stringWithFormat:@"XLOperation1 %zd",i];
            NSLog(@"XLOperation1 -%zd -- isCanceled = %zd-- executing=%zd -- isFinished = %zd -- %@", i, self.isCancelled,self.executing,self.isFinished,[NSThread currentThread]);
        }
    }
}
@catch (NSException *exception) {
    
}

3.2 并發的NSOperation

  • 如果需要創建一個并發的Operation,并手動執行,則需要重寫main,start,isCancelled,isReady,isConcurrent,isFinished,isExcluting等方法,并維護每個property的KVO,同時確保每個property的原子性,詳細使用示例可以去看SDWebImage中的SDWebImageDownloaderOperation使用
  • 使用步驟:
    • start: 所有并行的 Operations 都必須重寫這個方法,然后在你想要執行的線程中手動調用這個方法。注意:任何時候都不能調用父類的start方法。
    • main: 在start方法中調用,但是注意要定義獨立的自動釋放池與別的線程區分開。
    • isExecuting: 是否執行中,需要實現KVO通知機制。
    • isFinished: 是否已完成,需要實現KVO通知機制。
    • isConcurrent: 該方法現在已經由isAsynchronous方法代替,并且 NSOperationQueue 也已經忽略這個方法的值。
    • isAsynchronous: 該方法默認返回 NO ,表示非并發執行。并發執行需要自定義并且返回 YES。后面會根據這個返回值來決定是否并發。

參考資料:

NSOperation那些事

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容