iOS多線程編程--NSOperation

來源:互聯網
上載者:User

標籤:自動   imp   dem   sop   需要   exec   狀態   ati   非同步   

這篇文章寫得非常不錯,基礎用法都涉及到了,我把文章提到的例子都寫到了demo裡面,原文地址: iOS多線程--徹底學會多線程之『NSOperation』

demo下載:https://github.com/wangdachui/multithreading.git

1. NSOperation簡介

NSOperation是蘋果提供給我們的一套多線程解決方案。實際上NSOperation是基於GCD更高一層的封裝,但是比GCD更簡單易用、代碼可讀性也更高。

NSOperation需要配合NSOperationQueue來實現多線程。因為預設情況下,NSOperation單獨使用時系統同步執行操作,並沒有開闢新線程的能力,只有配合NSOperationQueue才能實現非同步執行。

因為NSOperation是基於GCD的,那麼使用起來也和GCD差不多,其中,NSOperation相當於GCD中的任務,而NSOperationQueue則相當於GCD中的隊列。NSOperation實現多線程的使用步驟分為三步:

  1. 建立任務:先將需要執行的操作封裝到一個NSOperation對象中。
  2. 建立隊列:建立NSOperationQueue對象。
  3. 將任務加入到隊列中:然後將NSOperation對象添加到NSOperationQueue中。

之後呢,系統就會自動將NSOperationQueue中的NSOperation取出來,在新線程中執行操作。

下面我們來學習下NSOperation和NSOperationQueue的基本使用。

2.NSOperation和NSOperationQueue的基本使用1. 建立任務

NSOperation是個抽象類別,並不能封裝任務。我們只有使用它的子類來封裝任務。我們有三種方式來封裝任務。

  1. 使用子類NSInvocationOperation
  2. 使用子類NSBlockOperation
  3. 定義繼承自NSOperation的子類,通過實現內部相應的方法來封裝任務。

在不使用NSOperationQueue,單獨使用NSOperation的情況下系統同步執行操作,下面我們學習以下任務的三種建立方式。

1. 使用子類 - NSInvocationOperation:
// 1.建立NSInvocationOperation對象NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];// 2.調用start方法開始執行操作[op start];- (void)run{    NSLog(@"------%@", [NSThread currentThread]);}

輸出結果:
2016-09-05 14:29:58.483 NSOperation[15834:2384555] ------<NSThread: 0x7fa3e2e05410>{number = 1, name = main}

從中可以看到,在沒有使用NSOperationQueue、單獨使用NSInvocationOperation的情況下,NSInvocationOperation在主線程執行操作,並沒有開啟新線程。

下邊再來看看NSBlockOperation。

2. 使用子類 - NSBlockOperation
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{    // 在主線程    NSLog(@"------%@", [NSThread currentThread]);}];[op start];

輸出結果:
2016-09-05 14:33:15.268 NSOperation[15884:2387780] ------<NSThread: 0x7fb2196012c0>{number = 1, name = main}

我們同樣可以看到,在沒有使用NSOperationQueue、單獨使用NSBlockOperation的情況下,NSBlockOperation也是在主線程執行操作,並沒有開啟新線程。

但是,NSBlockOperation還提供了一個方法addExecutionBlock:,通過addExecutionBlock:就可以為NSBlockOperation添加額外的操作,這些額外的操作就會在其他線程並發執行。

- (void)blockOperation{    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{        // 在主線程        NSLog(@"1------%@", [NSThread currentThread]);    }];        // 添加額外的任務(在子線程執行)    [op addExecutionBlock:^{        NSLog(@"2------%@", [NSThread currentThread]);    }];    [op addExecutionBlock:^{        NSLog(@"3------%@", [NSThread currentThread]);    }];    [op addExecutionBlock:^{        NSLog(@"4------%@", [NSThread currentThread]);    }];    [op start];}

輸出結果:
2016-09-05 14:36:59.353 NSOperation[15896:2390616] 1------<NSThread: 0x7ff633f03be0>{number = 1, name = main}
2016-09-05 14:36:59.354 NSOperation[15896:2390825] 2------<NSThread: 0x7ff633e24600>{number = 2, name = (null)}
2016-09-05 14:36:59.354 NSOperation[15896:2390657] 3------<NSThread: 0x7ff633c411e0>{number = 3, name = (null)}
2016-09-05 14:36:59.354 NSOperation[15896:2390656] 4------<NSThread: 0x7ff633f1d3e0>{number = 4, name = (null)}

可以看出,blockOperationWithBlock:方法中的操作是在主線程中執行的,而addExecutionBlock:方法中的操作是在其他線程中執行的。

3. 定義繼承自NSOperation的子類

先定義一個繼承自NSOperation的子類,重寫main方法
YSCOperation.h

#import <Foundation/Foundation.h>@interface YSCOperation : NSOperation@end

YSCOperation.m

#import "YSCOperation.h"@implementation YSCOperation/** * 需要執行的任務 */- (void)main{    for (int i = 0; i < 2; ++i) {        NSLog(@"1-----%@",[NSThread currentThread]);    }    }@end

然後使用的時候匯入標頭檔YSCOperation.h

// 建立YSCOperationYSCOperation *op1 = [[YSCOperation alloc] init];[op1 start];

輸出結果:
2016-09-05 18:15:59.674 NSOperation[16566:2501606] 1-----<NSThread: 0x7f8030d05150>{number = 1, name = main}
2016-09-05 18:15:59.675 NSOperation[16566:2501606] 1-----<NSThread: 0x7f8030d05150>{number = 1, name = main}

可以看出:在沒有使用NSOperationQueue、單獨使用自訂子類的情況下,是在主線程執行操作,並沒有開啟新線程。

下邊我們簡單講講NSOperationQueue的建立。

2. 建立隊列

和GCD中的並發隊列、串列隊列略有不同的是:NSOperationQueue一共有兩種隊列:主隊列、其他隊列。其中其他隊列同時包含了串列、並發功能。下邊是主隊列、其他隊列的基本建立方法和特點。

  • 主隊列
    • 凡是添加到主隊列中的任務(NSOperation),都會放到主線程中執行
      NSOperationQueue *queue = [NSOperationQueue mainQueue];
  • 其他隊列(非主隊列)
    • 添加到這種隊列中的任務(NSOperation),就會自動放到子線程中執行
    • 同時包含了:串列、並發功能
      NSOperationQueue *queue = [[NSOperationQueue alloc] init];
3. 將任務加入到隊列中

前邊說了,NSOperation需要配合NSOperationQueue來實現多線程。
那麼我們需要將建立好的任務加入到隊列中去。總共有兩種方法

  1. - (void)addOperation:(NSOperation *)op;
    • 需要先建立任務,再將建立好的任務加入到建立好的隊列中去
- (void)addOperationToQueue{    // 1.建立隊列    NSOperationQueue *queue = [[NSOperationQueue alloc] init];    // 2. 建立操作      // 建立NSInvocationOperation        NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];        // 建立NSBlockOperation        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{        for (int i = 0; i < 2; ++i) {            NSLog(@"1-----%@", [NSThread currentThread]);        }    }];    // 3. 添加操作到隊列中:addOperation:       [queue addOperation:op1]; // [op1 start]        [queue addOperation:op2]; // [op2 start]}- (void)run{    for (int i = 0; i < 2; ++i) {        NSLog(@"2-----%@", [NSThread currentThread]);    }}

輸出結果:
2016-09-05 17:06:00.241 NSOperationQueue[16201:2452281] 1-----<NSThread: 0x7fe4824080e0>{number = 3, name = (null)}
2016-09-05 17:06:00.241 NSOperationQueue[16201:2452175] 2-----<NSThread: 0x7fe482404a50>{number = 2, name = (null)}
2016-09-05 17:06:00.242 NSOperationQueue[16201:2452175] 2-----<NSThread: 0x7fe482404a50>{number = 2, name = (null)}
2016-09-05 17:06:00.241 NSOperationQueue[16201:2452281] 1-----<NSThread: 0x7fe4824080e0>{number = 3, name = (null)}

可以看出:NSInvocationOperation和NSOperationQueue結合後能夠開啟新線程,進行並發執行NSBlockOperation和NSOperationQueue也能夠開啟新線程,進行並發執行。

  1. - (void)addOperationWithBlock:(void (^)(void))block;
    • 無需先建立任務,在block中新增工作,直接將任務block加入到隊列中。
- (void)addOperationWithBlockToQueue{    // 1. 建立隊列    NSOperationQueue *queue = [[NSOperationQueue alloc] init];    // 2. 添加操作到隊列中:addOperationWithBlock:    [queue addOperationWithBlock:^{        for (int i = 0; i < 2; ++i) {            NSLog(@"-----%@", [NSThread currentThread]);        }    }];}

輸出結果:
2016-09-05 17:10:47.023 NSOperationQueue[16293:2457487] -----<NSThread: 0x7ffa6bc0e1e0>{number = 2, name = (null)}
2016-09-05 17:10:47.024 NSOperationQueue[16293:2457487] -----<NSThread: 0x7ffa6bc0e1e0>{number = 2, name = (null)}

可以看出addOperationWithBlock:和NSOperationQueue能夠開啟新線程,進行並發執行。

3. 控制串列執行和並存執行的關鍵

之前我們說過,NSOperationQueue建立的其他隊列同時具有串列、並發功能,上邊我們示範了並發功能,那麼他的串列功能是如何?的?

這裡有個關鍵參數maxConcurrentOperationCount,叫做最大並發數

  • 最大並發數:maxConcurrentOperationCount
    • maxConcurrentOperationCount預設情況下為-1,表示不進行限制,預設為並發執行。
    • maxConcurrentOperationCount為1時,進行串列執行。
    • maxConcurrentOperationCount大於1時,進行並發執行,當然這個值不應超過系統限制,即使自己設定一個很大的值,系統也會自動調整。
- (void)opetationQueue{    // 建立隊列    NSOperationQueue *queue = [[NSOperationQueue alloc] init];    // 設定最大並行作業數    //    queue.maxConcurrentOperationCount = 2;    queue.maxConcurrentOperationCount = 1; // 就變成了串列隊列    // 添加操作    [queue addOperationWithBlock:^{        NSLog(@"1-----%@", [NSThread currentThread]);        [NSThread sleepForTimeInterval:0.01];    }];    [queue addOperationWithBlock:^{        NSLog(@"2-----%@", [NSThread currentThread]);        [NSThread sleepForTimeInterval:0.01];    }];    [queue addOperationWithBlock:^{        NSLog(@"3-----%@", [NSThread currentThread]);        [NSThread sleepForTimeInterval:0.01];    }];    [queue addOperationWithBlock:^{        NSLog(@"4-----%@", [NSThread currentThread]);        [NSThread sleepForTimeInterval:0.01];    }];    [queue addOperationWithBlock:^{        NSLog(@"5-----%@", [NSThread currentThread]);        [NSThread sleepForTimeInterval:0.01];    }];    [queue addOperationWithBlock:^{        NSLog(@"6-----%@", [NSThread currentThread]);        [NSThread sleepForTimeInterval:0.01];    }];}

最大並發數為1輸出結果:
2016-09-05 17:21:54.124 NSOperationQueue[16320:2464630] 1-----<NSThread: 0x7fc892d0b3a0>{number = 2, name = (null)}
2016-09-05 17:21:54.136 NSOperationQueue[16320:2464631] 2-----<NSThread: 0x7fc892c0a7b0>{number = 3, name = (null)}
2016-09-05 17:21:54.148 NSOperationQueue[16320:2464630] 3-----<NSThread: 0x7fc892d0b3a0>{number = 2, name = (null)}
2016-09-05 17:21:54.160 NSOperationQueue[16320:2464631] 4-----<NSThread: 0x7fc892c0a7b0>{number = 3, name = (null)}
2016-09-05 17:21:54.171 NSOperationQueue[16320:2464631] 5-----<NSThread: 0x7fc892c0a7b0>{number = 3, name = (null)}
2016-09-05 17:21:54.184 NSOperationQueue[16320:2464630] 6-----<NSThread: 0x7fc892d0b3a0>{number = 2, name = (null)}

最大並發數為2輸出結果:
2016-09-05 17:23:36.030 NSOperationQueue[16331:2466366] 2-----<NSThread: 0x7fd729f0f270>{number = 3, name = (null)}
2016-09-05 17:23:36.030 NSOperationQueue[16331:2466491] 1-----<NSThread: 0x7fd729f4e290>{number = 2, name = (null)}
2016-09-05 17:23:36.041 NSOperationQueue[16331:2466367] 3-----<NSThread: 0x7fd729d214e0>{number = 4, name = (null)}
2016-09-05 17:23:36.041 NSOperationQueue[16331:2466366] 4-----<NSThread: 0x7fd729f0f270>{number = 3, name = (null)}
2016-09-05 17:23:36.053 NSOperationQueue[16331:2466366] 6-----<NSThread: 0x7fd729f0f270>{number = 3, name = (null)}
2016-09-05 17:23:36.053 NSOperationQueue[16331:2466511] 5-----<NSThread: 0x7fd729e056c0>{number = 5, name = (null)}

可以看出:當最大並發數為1時,任務是按順序串列執行的。當最大並發數為2時,任務是並發執行的。而且開啟線程數量是由系統決定的,不需要我們來管理。這樣看來,是不是比GCD還要簡單了許多?

4. 操作依賴

NSOperation和NSOperationQueue最迷人的地方是它能添加操作之間的依賴關係。比如說有A、B兩個操作,其中A執行完操作,B才能執行操作,那麼就需要讓B依賴於A。具體如下:

- (void)addDependency{    NSOperationQueue *queue = [[NSOperationQueue alloc] init];    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"1-----%@", [NSThread  currentThread]);    }];    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{        NSLog(@"2-----%@", [NSThread  currentThread]);    }];    [op2 addDependency:op1];    // 讓op2 依賴於 op1,則先執行op1,在執行op2    [queue addOperation:op1];    [queue addOperation:op2];}

輸出結果:
2016-09-05 17:51:28.811 操作依賴[16423:2484866] 1-----<NSThread: 0x7fc138e1e7c0>{number = 2, name = (null)}
2016-09-05 17:51:28.812 操作依賴[16423:2484866] 2-----<NSThread: 0x7fc138e1e7c0>{number = 2, name = (null)}

可以看到,無論運行幾次,其結果都是op1先執行,op2後執行。

5. 一些其他方法
  • - (void)cancel; NSOperation提供的方法,可取消單個操作
  • - (void)cancelAllOperations; NSOperationQueue提供的方法,可以取消隊列的所有操作
  • - (void)setSuspended:(BOOL)b; 可設定任務的暫停和恢複,YES代表暫停隊列,NO代表恢複隊列
  • - (BOOL)isSuspended; 判斷暫停狀態

  • 注意:

    • 這裡的暫停和取消並不代表可以將當前的操作立即取消,而是噹噹前的操作執行完畢之後不再執行新的操作。
    • 暫停和取消的區別就在於:暫停操作之後還可以恢複操作,繼續向下執行;而取消操作之後,所有的操作就清空了,無法再接著執行剩下的操作。

iOS多線程編程--NSOperation(轉)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.