NSOperation 和 NSOperationQueue

NSOperation和NSOperationQueue爲咱們提供面向對象方式的多線程編程方式。html

NSOperation

NSOperation是一個抽象類,咱們能夠用它來封裝一系列操做的代碼和數據。由於它是抽象,咱們沒法直接使它,而是使用它的子類。這個子類要麼你本身定義,要麼使用系統定義好的(NSInvocationOperation或者NSBlockOperation)。儘管它是抽象的,可是,NSOperation可讓咱們只須要關心任務的實現過程,而沒必要關心它是如何確保與其餘系統對象正確運轉的。 ios

NSOperation對象是"一次性"的,它一旦執行了它的任務以後,就不能被用來再次執行了。將NSOperation的對象添加到一個操做隊列中(NSOperationQueue的一個實例)。這個隊列會自動執行它所包含的操做,可能直接開闢一個線程執行,也可能間接地使用libdispatch庫。 編程

若是不想使用隊列,能夠調用NSOperation的實例方法start來執行操做。若是手動控制操做的執行,咱們須要考慮更多的內容。由於若是執行一個沒有處在ready狀態的操做,系統會拋出異常。多線程

Operation之間的依賴

依賴能夠很好地控制操做的執行前後順序。咱們能夠經過調用addDependency:removeDependency:來爲一個操做添加或移除依賴。默認地,若是一個操做的依賴都沒有執行完,那麼它不會進入ready狀態。一旦它的依賴執行完成,它就能夠當即被執行。 app

一個操做不會判斷它的依賴是否成功地執行。(取消一個操做近似於標記這個操做已經完成。)一個有依賴的操做在它的依賴被取消或者未執行成功的狀況下是否被繼續執行取決於咱們本身。這須要咱們給操做加入一些錯誤跟蹤能力,以保證咱們及時發現問題。異步

KVO相關的屬性

NSOperation類提供了一系列可用於KVO編程的屬性。async

  • isCancelled - 只讀函數

  • isAsynchronous - 只讀spa

  • isExecuting - 只讀線程

  • isFinished - 只讀

  • isReady - 只讀

  • dependencies - 只讀

  • queuePriority - 可讀可寫

  • completionBlock - 可讀可寫

雖然給這些屬性添加觀察者,可是咱們不要它們和UI元素進行捆綁。UI相關的代碼只能在主線程執行,而NSOperation對象的操做可能在任一線程中執行,因此通知也會出如今任一線程裏。若是和UI元素捆綁,那就會形成主線程的阻塞。

NSOperation的一些方法和屬性

方法:

  • start。可讓NSOperation直接執行。

  • cancel。取消NSOperation的操做。

  • addDependency: 和 removeDependency:。爲NSOperation添加依賴或移除依賴。

屬性:

  • queuePriority。在隊列中的優先級。有NSOperationQueuePriorityVeryLowNSOperationQueuePriorityLowNSOperationQueuePriorityNormalNSOperationQueuePriorityHighNSOperationQueuePriorityVeryHigh5個值。若是沒有明確設置,默認是NSOperationQueuePriorityNormal

  • qualityOfService。表示當前NSOperation的相對重要性,以獲取不一樣程度的系統資源。

Operation的狀態

NSOperation類還提供一系列屬性表示操做目前的狀態。

  • cancelled。表示是否被取消。默認值是NO。能夠經過調用cancel方法取消操做的執行,這個屬性的值被置爲YES。一旦取消,操做就會進入finished狀態。

  • executing。表示操做是否正在執行。

  • finished。表示操做是否執行完畢。

  • concurrent、asynchronous。這兩個屬性都表示操做是不是異步的。

  • ready。表示當前的操做是否能夠開始執行。

注意:上面的幾個屬性都是隻讀的。

當咱們繼承NSOperation,實現子類的時候,在main函數的開頭要判斷cancelled的值,以儘快退出一個已經取消的操做。同時,必需要從新實現executingfinishedasynchronousready這幾個屬性。

目前,系統提供的NSInvocationOperationNSBlockOperation兩個子類基本能夠知足個人需求,尚未深刻研究重寫時的注意點,會在之後補充上。

NSInvocationOperation

這是系統提供的一個NSOperation的子類。經過@selector,將操做包含到自身。

- (instancetype)initWithTarget:(id)target
                      selector:(SEL)sel
                        object:(id)arg

target表示在那個類中實現了SEL的方法。SEL能夠帶0或1個參數。若是參數爲0個,arg置爲nil,不然就設爲SEL所帶的參數。

直接給個簡單的例子。

//定義一個方法,輸出「One」。
- (void)outputOne {
    NSLog(@"One");
}
//返回值爲"Two"。
- (NSString*)returnTwo {
    return @"Two";
}
//用上面的兩個方法分別建立NSInvocationOperation實例
NSInvocationOperation *outputOne = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(outputOne) object:nil];
NSInvocationOperation *returnTwo = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(returnTwo) object:nil];

NSBlockOperation

這是系統提供的另外一個NSOperation的子類,將要包含的操做放到block中。

//這是一個類方法,而建立NSInvocationOperation實例是一個實例方法
+ (instancetype)blockOperationWithBlock:(void (^)(void))block

這裏一樣直接給出一個例子。

//聯繫上一段代碼,這個NSBlockOperation的實例輸出`returnTwo`的`返回值`和`target`。
NSBlockOperation *outputTwo = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"%@",[returnTwo result]);
    }];
[outputTwo addExecutionBlock:^{
    NSLog(@"%@",[[returnTwo invocation] target]);
}];

對於NSBlockOperation對象,能夠調用- addExecutionBlock:來添加一些操做。

NSOperationQueue

NSOperationQueue管理隊列中NSOperation的執行順序。當一個NSOperation被添加到隊列中後,除非它被取消或者執行完操做,不然會一直存在於隊列中。存在於隊列中NSOperation根據它們自身的優先級和依賴關係,肯定執行的順序。一個應用能夠建立多個隊列,NSOperation能夠添加到任意一個隊列中。

NSOperation的依賴決定NSOperation執行的絕對順序,即便它們不在同一個隊列中。當NSOperation的依賴沒有徹底執行結束時,它是不會進入ready狀態。對於那些進入ready狀態的NSOperation,隊列會選擇一個優先級相對最高的那個NSOperation來執行。

隊列中的NSOperation不能直接被移除,直到它被標記成finished。即便NSOperation被標記爲finished,也不意味着它被執行完成。由於它是能夠被取消的。對於在隊列中處於ready,還沒被執行的NSOperation,會自動調用start方法將操做執行完畢。

NSOperationQueue老是開闢一個新的線程來執行隊列裏的NSOperation。NSOperationQueue使用libdispatch庫來初始化執行NSOperation的隊列。所以,NSOperation老是在不一樣的線程裏被執行,不管它是否被指定是同步或異步。

能夠用於KVO的一些屬性

NSOperationQueue一樣有一些屬性,能夠用於KVO編程。

  • operations - 只讀

  • operationCount - 只讀

  • maxConcurrentOperationCount - 可讀可寫

  • suspended - 可讀可寫

  • name - 可讀可寫

一樣地,這些屬性不要和UI元素進行觀察者綁定。

經常使用的方法和屬性

方法:

+ mainQueue
+ currentQueue

這兩個方法能夠分別獲取到主線程和當前正在執行的線程。

//添加一個NSOperation對象到隊列中
- (void)addOperation:(NSOperation *)operation

若是被添加的NSOperation對象已經存在於其餘的隊列中,會拋出NSInvalidArgumentException異常。若是一個NSOperation對象的isExecuting或者isFinished爲YES,一樣會拋出NSInvalidArgumentException異常。

- cancelAllOperations

取消當前隊列中全部的NSOperation。

屬性:

@property NSInteger maxConcurrentOperationCount

隊列一次性能夠執行的NSOperation的最大數目。默認值是NSOperationQueueDefaultMaxConcurrentOperationCount。系統會根據當前的設備裝備的情況動態設置最大執行數目。

這個屬性隻影響當前隊列一次性可執行的NSOperation最大個數,其餘隊列按照其本身的最大可執行數目進行。

@property(getter=isSuspended) BOOL suspended

這個屬性可使當前隊列中的NSOperation延遲執行。默認值是NO。當被設爲YES時,當前隊列中還未執行的NSOperation就會延時執行,直到這個值從新被設爲NO。

一個簡單的例子

這裏,咱們繼續使用上面給出的代碼。

首先,定義幾個操做

//輸出`One`
- (void)outputOne {
    NSLog(@"One");
}
//返回`Two`
- (NSString*)returnTwo {
    return @"Two";
}
//顯示一個背景色爲紅色的Button
- (void)displayButton {
    //    [[NSOperationQueue mainQueue] waitUntilAllOperationsAreFinished];
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 100, [UIScreen mainScreen].bounds.size.width, 50)];
    [button setBackgroundColor:[UIColor redColor]];
    [self.view addSubview:button];
}

再實例化NSOperation對象

//用NSInvocationOperation實例化操做對象
NSInvocationOperation *outputOne = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(outputOne) object:nil];
NSInvocationOperation *returnTwo = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(returnTwo) object:nil];
NSInvocationOperation *display = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(displayButton) object:nil];
//用NSBlockOperation實例化操做對象
NSBlockOperation *outputTwo = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"%@",[returnTwo result]);
}];
[outputTwo addExecutionBlock:^{
    NSLog(@"%@",[[returnTwo invocation] target]);
}];

而後給操做設置依賴

[outputTwo addDependency:outputOne];
[outputTwo addDependency:returnTwo];
[display addDependency:outputTwo];
//定義以後,幾個操做的執行順序`可能`是 outputOne -> returnTwo -> outputTwo -> display。對於outputOne和returnTwo這兩個操做,由於它們的優先級是默認值,也沒有其餘的依賴,因此誰先誰後是隨機的。

最後,將這些NSOperation的對象添加到相應的隊列中去。

//初始化一個自定義的隊列
NSOperationQueue *customQueue = [[NSOperationQueue alloc] init];
//獲取到主線程隊列
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
//好像主線程設置suspended屬性爲YES後,依然會執行主線程中的操做
mainQueue.suspended = YES;
//將display添加到主線程中
[mainQueue addOperation:display];  
//將outputOne,returnTwo,outputTwo添加到自定義的隊列中
[customQueue addOperation:returnTwo];
//這裏設置爲YES,若是沒有再次將`suspended`設爲NO,那麼只會在控制檯輸出`One`
customQueue.suspended = YES;
[customQueue addOperation:outputOne];
[customQueue addOperation:outputTwo];
//customQueue.suspended = NO;

參考文檔:
NSOperationQueue Class Reference
NSOperation Class Reference
NSBlockOperation
NSInvocationOperation

相關文章
相關標籤/搜索