多線程-NSOperation和NSOperationQueue


NSOperation

NSOperation類是用來封裝在單個任務(wù)相關(guān)的代碼和數(shù)據(jù)的抽象類桥温。NSOperation 是蘋果公司對 GCD 的封裝蛹屿,完全面向?qū)ο笠缍梗允褂闷饋砀美斫狻?br> ** 因為它是用來封裝任務(wù)的,大家可以看到 NSOperation 和 NSOperationQueue 分別對應(yīng) GCD 的 任務(wù) 和 隊列拉宗,
但是NSOperation本身又有執(zhí)行多線程的能力跟GCD里的任務(wù)還是有區(qū)別的** 车荔。

操作步驟也很好理解:

  • 將要執(zhí)行的任務(wù)封裝到一個 NSOperation 對象中渡冻。
  • 將此任務(wù)添加到一個 NSOperationQueue 對象中。
    然后系統(tǒng)就會自動在執(zhí)行任務(wù)忧便。
創(chuàng)建任務(wù)

NSOperation 只是一個抽象類族吻,所以不能封裝任務(wù)。但它有 2 個子類用于封裝任務(wù)珠增。分別是:NSInvocationOperation 和 NSBlockOperation 超歌。創(chuàng)建一個 Operation 后,需要調(diào)用 start 方法來啟動任務(wù)切平,它會默認在當前隊列同步執(zhí)行握础。當然你也可以在中途取消一個任務(wù)辐董,只需要調(diào)用其 cancel 方法即可悴品。

 #.創(chuàng)建NSInvocationOperation對象
  NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
  //開始執(zhí)行
 [operation start];
 # 創(chuàng)建一個NSOperation不應(yīng)該直接調(diào)用start方法(如果直接start則會在主線程中調(diào)用)而是應(yīng)該放到NSOperationQueue中啟動。

 #.創(chuàng)建NSBlockOperation對象
 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"%@", [NSThread currentThread]);
  }];
  //開始任務(wù)
  [operation start];

注意:之前說過這樣的任務(wù),默認會在當前線程執(zhí)行苔严。但是 NSBlockOperation 還有一個方法:addExecutionBlock: 定枷,通過這個方法可以給 Operation 添加多個執(zhí)行 Block。這樣 Operation 中的任務(wù) 會并發(fā)執(zhí)行届氢,它會 在主線程和其它的多個線程 執(zhí)行這些任務(wù).欠窒。并且ddExecutionBlock 方法必須在 start() 方法之前執(zhí)行,否則就會報錯退子。

# 創(chuàng)建NSBlockOperation對象
  NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
      NSLog(@"%@", [NSThread currentThread]);
  }];
# 添加多個Block
  for (NSInteger i = 0; i < 5; i++) {
      [operation addExecutionBlock:^{
          NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
      }];
  }
  #開始任務(wù)岖妄,你會發(fā)現(xiàn),會在多個線程中打印寂祥,其中包括主線程荐虐。
 [operation start];

自定義Operation

除了上面的兩種 Operation 以外,我們還可以自定義 Operation丸凭。自定義 Operation 需要繼承 NSOperation 類福扬,并實現(xiàn)其 main() 方法,因為在調(diào)用 start() 方法的時候惜犀,內(nèi)部會調(diào)用 main() 方法完成相關(guān)邏輯铛碑。所以如果以上的兩個類無法滿足你的欲望的時候,你就需要自定義了虽界。你想要實現(xiàn)什么功能都可以寫在里面汽烦。除此之外,你還需要實現(xiàn) cancel() 在內(nèi)的各種方法莉御。

創(chuàng)建隊列

我們可以調(diào)用一個 NSOperation 對象的 start() 方法來啟動這個任務(wù)刹缝,這個默認是 同步執(zhí)行 的。就算是 addExecutionBlock 方法颈将,也會在 當前線程和其他線程 中執(zhí)行梢夯,也就是說還是會占用當前線程。如果你不想這個任務(wù)在主線程中執(zhí)行(代碼默認情況下都在主線程中執(zhí)行晴圾。)這是就要用到隊列 NSOperationQueue 颂砸。按類型來說的話一共有兩種類型:主隊列、其他隊列死姚。只要添加到隊列人乓,會自動調(diào)用任務(wù)的 start() 方法。

  • 主隊列
    主隊列是串行隊列都毒,添加到主隊列的任務(wù)都會一個接一個地排著隊在主線程處理色罚。
    # 獲取到主隊列
    NSOperationQueue *queue = [NSOperationQueue mainQueue];
  • 其他創(chuàng)建的隊列
    主隊列比較特殊,所以會單獨有一個類方法來獲得主隊列账劲。并且其他隊列的任務(wù)會在其他線程并行執(zhí)行戳护。請注意這里是并行執(zhí)行金抡。

    #1.創(chuàng)建一個其他隊列    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    #2.創(chuàng)建NSBlockOperation對象
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"%@", [NSThread currentThread]);
    }];
     #.添加多個Block
    for (NSInteger i = 0; i < 5; i++) {
        [operation addExecutionBlock:^{
            NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
        }];
    }
     #.隊列添加任務(wù)
    [queue addOperation:operation];
    
NSOperationQueue的一些特殊使用
  • 設(shè)置最大并發(fā)數(shù)

我們將 NSOperationQueue 與 GCD的隊列 相比較就會發(fā)現(xiàn),這里沒有串行隊列腌且,那如果我想要10個任務(wù)在其他線程串行的執(zhí)行的話梗肝,NSOperationQueue 有一個參數(shù) maxConcurrentOperationCount 最大并發(fā)數(shù),用來設(shè)置最多可以讓多少個任務(wù)同時執(zhí)行铺董。當你把它設(shè)置為 1 的時候巫击,就相當于是串行隊列了

  • NSOperationQueue 添加一個任務(wù)到隊列
    - (void)addOperationWithBlock:(void (^)(void))block; 這樣就可以添加一個任務(wù)到隊列中了精续。

  • NSOperation 之間添加依賴

    NSOperation 有一個非常實用的功能坝锰,那就是對任務(wù)添加依賴。比如有 3 個任務(wù):A: 從服務(wù)器上下載一張圖片重付,B:給這張圖片加個水印什黑,C:把圖片返回給服務(wù)器。這時就可以用到依賴了:

    #1.任務(wù)一:下載圖片
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"下載圖片 - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
    }];
    
    #2.任務(wù)二:打水印
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"打水印   - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
    }];
    
    #3.任務(wù)三:上傳圖片
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"上傳圖片 - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
    }];
    
    #4.設(shè)置依賴
    [operation2 addDependency:operation1];      //任務(wù)二依賴任務(wù)一
    [operation3 addDependency:operation2];      //任務(wù)三依賴任務(wù)二
    
    #5.創(chuàng)建隊列并加入任務(wù)
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
    

    注意
    A.不能添加相互依賴堪夭,會死鎖愕把,比如 A依賴B,B依賴A森爽。
    B.可以使用 removeDependency 來解除依賴關(guān)系恨豁。
    C.可以在不同的隊列之間依賴,依賴是添加到任務(wù)身上的爬迟,和隊列沒關(guān)系橘蜜。

  • 線程的掛起

    #pragma mark - 線程的掛起
    #暫停繼續(xù)(對隊列的暫停和繼續(xù)),掛起的是隊列付呕,不會影響已經(jīng)在執(zhí)行的操作
    - (IBAction)pause:(UIButton *)sender {
    //判斷操作的數(shù)量计福,當前隊列里面是不是有操作?
    if (self.opQueue.operationCount == 0) {
      NSLog(@"當前隊列沒有操作");
      return;
    }
    self.opQueue.suspended = !self.opQueue.isSuspended;
    if (self.opQueue.suspended) {
      NSLog(@"暫停");
    }else{
      NSLog(@"繼續(xù)");
    }
    

    }

  • 取消隊列里的所有操作

    #pragma mark - 取消隊列里的所有操作
    - (IBAction)cancelAll:(UIButton *)sender {
    # 只能取消所有隊列的里面的操作徽职,正在執(zhí)行的無法取消
    # 取消操作并不會影響隊列的掛起狀態(tài)
    [self.opQueue cancelAllOperations];
    NSLog(@"取消隊列里所有的操作");
    //取消隊列的掛起狀態(tài)
    #(只要是取消了隊列的操作象颖,我們就把隊列處于不掛起狀態(tài),以便于后續(xù)的開始)
    self.opQueue.suspended = NO;
    }
    

其他方法

以上就是一些主要方法, 下面還有一些常用方法需要大家注意:

#  NSOperation
BOOL executing; //判斷任務(wù)是否正在執(zhí)行
BOOL finished; //判斷任務(wù)是否完成
#當一個 operation 被取消時,它的 completion block 仍然會執(zhí)行姆钉,
#所以我們需要在真正執(zhí)行代碼前檢查一下 isCancelled 方法的返回值说订。
- void (^completionBlock)(void); //用來設(shè)置完成后需要執(zhí)行的操作
 
- (void)cancel; //取消任務(wù)
- (void)waitUntilFinished; //阻塞當前線程直到此任務(wù)執(zhí)行完畢

#  NSOperationQueue
NSUInteger operationCount; //獲取隊列的任務(wù)數(shù)
- (void)cancelAllOperations; //取消隊列中所有的任務(wù)
#阻塞當前線程直到此隊列中的所有任務(wù)執(zhí)行完畢
# **可以用來處理所有任務(wù)完成后的事件**
- (void)waitUntilAllOperationsAreFinished; 
[queue setSuspended:YES]; // 暫停queue
[queue setSuspended:NO]; // 繼續(xù)queue
NSOperation和GCD的區(qū)別和類似的地方
  • GCD是純C語言的API,NSOperationQueue是基于GCD的OC版本封裝
  • GCD只支持FIFO的隊列潮瓶,NSOperationQueue可以很方便地調(diào)整執(zhí)行順序陶冷、設(shè)置最大并發(fā)數(shù)量
  • NSOperationQueue可以在輕松在Operation間設(shè)置依賴關(guān)系,而GCD需要寫很多的代碼才能實現(xiàn)
  • NSOperationQueue支持KVO毯辅,可以監(jiān)測operation是否正在執(zhí)行(isExecuted)埂伦、是否結(jié)束(isFinished),是否取消(isCanceld)
  • GCD的執(zhí)行速度比NSOperationQueue快
    **任務(wù)之間沒有什么依賴關(guān)系思恐,而是需要更高的并發(fā)能力:GCD **
    任務(wù)之間有依賴沾谜、或者要監(jiān)聽任務(wù)的執(zhí)行情況膊毁、需要暫停、繼續(xù)任務(wù):NSOperationQueue

小結(jié)

以上就是關(guān)于 NSOperation和NSOperationQueue 的主要知識了类早,后期如果有更多內(nèi)容會同步更新媚媒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嗜逻,一起剝皮案震驚了整個濱河市涩僻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌栈顷,老刑警劉巖逆日,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異萄凤,居然都是意外死亡室抽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門靡努,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坪圾,“玉大人,你說我怎么就攤上這事惑朦∈扌梗” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵漾月,是天一觀的道長病梢。 經(jīng)常有香客問我,道長梁肿,這世上最難降的妖魔是什么蜓陌? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮吩蔑,結(jié)果婚禮上钮热,老公的妹妹穿的比我還像新娘。我一直安慰自己烛芬,他們只是感情好霉旗,可當我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蛀骇,像睡著了一般厌秒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上擅憔,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天鸵闪,我揣著相機與錄音,去河邊找鬼暑诸。 笑死蚌讼,一個胖子當著我的面吹牛辟灰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播篡石,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼芥喇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了凰萨?” 一聲冷哼從身側(cè)響起继控,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎胖眷,沒想到半個月后武通,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡珊搀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年冶忱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片境析。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡囚枪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出劳淆,到底是詐尸還是另有隱情链沼,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布憔儿,位于F島的核電站忆植,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏谒臼。R本人自食惡果不足惜朝刊,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜈缤。 院中可真熱鬧拾氓,春花似錦、人聲如沸底哥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽趾徽。三九已至续滋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間孵奶,已是汗流浹背疲酌。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人朗恳。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓湿颅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親粥诫。 傳聞我的和親對象是個殘疾皇子油航,可洞房花燭夜當晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容