iOS 多線程之NSOpreation

圖1.png

NSOpreation、NSOperationQueue簡介

  • NSOperation、NSOperationQueue 是蘋果提供給咱們的一套多線程解決方案。
  • NSOperation、NSOperationQueue 是基於 GCD 更高一層的封裝,徹底面向對象。
  • 雖然是基於GCD封裝,可是比 GCD 更簡單易用、代碼可讀性也更高。
  • GCD 中的一些概念一樣適用於 NSOperation、NSOperationQueue。在 NSOperation、NSOperationQueue 中也有相似的任務(操做)和隊列(操做隊列)的概念。
  • 在 NSOperation 中,因爲NSOperation是一種抽象類,因此在使用過程當中,咱們須要用NSOperation的子類 NSInvocationOperation、NSBlockOperation。

啥是抽象類? 抽象類是不完整的,它只能用做基類。在面向對象方法中,抽象類主要用來進行類型隱藏和充當全局變量的角色。 來自百度百科數組

NSOpreation、NSOperationQueue使用

建立隊列NSOperationQueue
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
複製代碼
建立操做NSOpreation
  • NSInvocationOperation
NSInvocationOperation *io = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(InvocationOperationSelector) object:nil];

    [io start];    

    -(void)InvocationOperationSelector{
        NSLog(@"這是NSInvocationOperation執行的任務 %@",[NSThread currentThread]);
    }
複製代碼

輸出結果 bash

image.png

可知,在操做沒有添加到隊列中,操做直接start,任務是在主線程上執行,沒有開啓線程。多線程

  • NSBlockOperation
NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"這是NSBlockOperation執行的任務 %@",[NSThread currentThread]);
    }];
    
    [bo start];
複製代碼

輸出結果 併發

image.png
從結果來看,跟 NSInvocationOperation同理。嘗試將操做添加到隊列中看看輸出結果。

  • NSBlockOperation添加額外的任務
NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"這是NSBlockOperation執行的任務 %@",[NSThread currentThread]);
    }];
    
    [bo addExecutionBlock:^{
        NSLog(@"這是讓NSBlockOperation另外執行的任務 %@",[NSThread currentThread]);
    }];
    
    [bo addExecutionBlock:^{
        NSLog(@"這是讓NSBlockOperation另外執行的任務2 %@",[NSThread currentThread]);
    }];
    
    [bo start];
複製代碼

輸出結果 函數

image.png
經過輸出結果可知,給操做添加額外任務,並不會讓這些任務按順序執行。可是初始化的任務仍然在主線程執行,另外兩個任務分別建立了線程。 因此 NSBlockOperation是否建立線程,取決於當前須要執行的任務數。 那麼, blockOperationWithBlock這裏面的任務就必定會在主線程執行嗎?咱們給上面的代碼加點戲,再看看打印結果。

NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"這是NSBlockOperation執行的任務 %@",[NSThread currentThread]);
        }
    }];
    
    [bo addExecutionBlock:^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"這是讓NSBlockOperation另外執行的任務 %@",[NSThread currentThread]);
        }
    }];
    
    [bo addExecutionBlock:^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"這是讓NSBlockOperation另外執行的任務2 %@",[NSThread currentThread]);
        }
    }];
    [bo start];
複製代碼

打印結果 ui

image.png
細心的小朋友已經發現了, blockOperationWithBlock裏面的任務,並無在主線程執行啦。

因此,總結一下
1.NSBlockOperation是否建立線程,取決於當前須要執行的任務數。
2.blockOperationWithBlock裏面的任務,並無不必定主線程執行啦。spa

添加操做(NSOpreation)到隊列(NSOperationQueue)
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    NSInvocationOperation *io = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(InvocationOperationSelector) object:nil];
   
     NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"這是NSBlockOperation執行的任務 %@",[NSThread currentThread]);
        }
    }];
    [queue addOperation:io];
    [queue addOperation:bo];
複製代碼

輸出結果 線程

image.png

可見,兩個操做都分別建立了number爲3和4的線程,再也不是主線程。3d

注意:start不能跟addOperation同時使用,不然會致使崩潰 code

image.png

添加、移除依賴

- (void)addDependency:(NSOperation *)op; 添加依賴,使當前操做依賴於操做 op 的完成。
- (void)removeDependency:(NSOperation *)op;移除依賴,取消當前操做對操做 op 的依賴。

NSOperationQueue *queue = [[NSOperationQueue alloc]init];

    NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"這是bo1執行的任務 %@",[NSThread currentThread]);
    }];
    NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"這是bo2執行的任務 %@",[NSThread currentThread]);
    }];
    NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"這是bo3執行的任務 %@",[NSThread currentThread]);
    }];
    //bo1依賴於bo2
    [bo1 addDependency:bo2];
    //bo2依賴於bo3
    [bo2 addDependency:bo3];

    [queue addOperations:@[bo1,bo2,bo3] waitUntilFinished:YES];
    
    NSLog(@"若是上面waitUntilFinished是YES,我就會最後執行");
複製代碼

輸出結果

image.png
waitUntilFinished這裏面傳入的BOOL 值將會影響下面的代碼執行順序,若是是YES,將會造成一個相似同步函數、柵欄函數的同步功能。等隊列中的任務中都執行結束後才執行下面的代碼。

#####NSOperationQueue控制最大併發數(實現同步)

NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    queue.maxConcurrentOperationCount = 1;
    
    NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i<5; i++) {
            NSLog(@"第%d次 執行任務 %@",i+1,[NSThread currentThread]);
        }
    }];
    
    [bo addExecutionBlock:^{
        for (int i = 0; i<5; i++) {
            NSLog(@"第%d次 執行額外任務 %@",i+1,[NSThread currentThread]);
        }
    }];
    
    [queue addOperation:bo];
複製代碼

先看輸出結果

image.png
從輸出結果能夠看出來一些東西
1.任務並非先執行 任務後執行 額外任務,而是相互穿插。
2.雖然跟理想中的結果不太同樣,可是不論 任務額外任務,卻按照12345的順序,老老實實的執行。
因此

這裏 maxConcurrentOperationCount 控制的不是併發線程的數量,而是一個隊列中同時能併發執行任務的最大數。並且一個任務也並不是只能在一個線程中運行。

線程間通信
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(10, 100, 200, 10)];
    [self.view addSubview:label];
    
    NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        NSString * str = @"字符串賦值";
        NSLog(@"這是子線程%@",[NSThread currentThread]);
        [[NSOperationQueue mainQueue]addOperationWithBlock:^{
            NSLog(@"回到主線程刷新UI%@",[NSThread currentThread]);
            label.text = str;
        }];
    }];
    [queue addOperation:bo];
複製代碼

打印以及顯示結果

image.png

NSOperation、NSOperationQueue 經常使用屬性和方法概括

NSOperation 經常使用屬性和方法

1.取消操做方法
- (void)cancel; 可取消操做,實質是標記 isCancelled 狀態。

2.判斷操做狀態方法
- (BOOL)isFinished; 判斷操做是否已經結束。
- (BOOL)isCancelled; 判斷操做是否已經標記爲取消。
- (BOOL)isExecuting; 判斷操做是否正在在運行。
- (BOOL)isReady; 判斷操做是否處於準備就緒狀態,這個值和操做的依賴關係相關。

3.操做同步
- (void)waitUntilFinished;阻塞當前線程,直到該操做結束。可用於線程執行順序的同步。
- (void)setCompletionBlock:(void (^)(void))block;completionBlock 會在當前操做執行完畢時執行 completionBlock。
- (void)addDependency:(NSOperation *)op;添加依賴,使當前操做依賴於操做 op 的完成。
- (void)removeDependency:(NSOperation *)op; 移除依賴,取消當前操做對操做 op 的依賴。
@property (readonly, copy) NSArray<NSOperation *> *dependencies; 在當前操做開始執行以前完成執行的全部操做對象數組。

NSOperationQueue 經常使用屬性和方法

1.取消/暫停/恢復操做
- (void)cancelAllOperations; 能夠取消隊列的全部操做。
- (BOOL)isSuspended; 判斷隊列是否處於暫停狀態。 YES 爲暫停狀態,NO 爲恢復狀態。
- (void)setSuspended:(BOOL)b; 可設置操做的暫停和恢復,YES 表明暫停隊列,NO 表明恢復隊列。

2.操做同步
- (void)waitUntilAllOperationsAreFinished; 阻塞當前線程,直到隊列中的操做所有執行完畢。

3.添加/獲取操做
- (void)addOperationWithBlock:(void (^)(void))block;向隊列中添加一個 NSBlockOperation 類型操做對象。
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait; 向隊列中添加操做數組,wait 標誌是否阻塞當前線程直到全部操做結束
- (NSArray *)operations;當前在隊列中的操做數組(某個操做執行結束後會自動從這個數組清除)。
- (NSUInteger)operationCount;當前隊列中的操做數。

4.獲取隊列
+ (id)currentQueue; 獲取當前隊列,若是當前線程不是在 NSOperationQueue 上運行則返回 nil。
+ (id)mainQueue;獲取主隊列。

部分摘取自西單_夜未央

相關文章
相關標籤/搜索