NSOperation 和 NSOperationQueue 基本使用

NSOperation陪踩、NSOperationQueue 簡介

  • NSOperation律适、NSOperationQueue是基于GCD更高一層的封裝啃奴,完全面向對象
  • NSOperation 和 NSOperationQueue 分別對應 GCD 的 任務 和 隊列,但是NSOperation跟GCD的任務還是有一些區(qū)別的
  • NSOperation --> 操作, NSOperationQueue --> 隊列
  • 操作步驟:
    1. 將要執(zhí)行的任務封裝到一個 NSOperation 對象中
    2. 將此任務添加到一個 NSOperationQueue 對象中

一旺遮、NSOperation (操作):

  • NSOperation是個抽象類医寿,不能封裝操作栏赴,只能使用它的子類來封裝操作,或者自定義子類來封裝操作靖秩。子類分別是:NSInvocationOperationNSBlockOperation
  • 創(chuàng)建一個 Operation 后须眷,需要調用 start 方法來啟動任務,它會默認在當前隊列同步執(zhí)行沟突。當然你也可以在中途取消一個任務花颗,只需要調用其 cancel 方法即可
  • 子類NSInvocationOperation
- (void)op1 {
    // 1.創(chuàng)建 NSInvocationOperation 對象
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task) object:nil];
    // 2.調用 start 方法開始執(zhí)行操作
    [op start];
}
- (void)task1 {
    for (int i = 0; i < 2; i++) {
        sleep(2); // 模擬耗時操作
        NSLog(@"task1---%@", [NSThread currentThread]); // 打印當前線程
    }
}

輸出結果:

image.png

可以看到:在沒有使用NSOperationQueue且在主線程中單獨使用使用子類 NSInvocationOperation執(zhí)行一個操作的情況下,操作是在當前線程執(zhí)行的惠拭,并沒有開啟新線程

  • 子類NSBlockOperation
- (void)op2 {
   // 1.創(chuàng)建 NSBlockOperation 對象
   NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
       for (int i = 0; i < 2; i++) {
           sleep(2); // 模擬耗時操作
           NSLog(@"op2---%@", [NSThread currentThread]); // 打印當前線程
       }
   }];
   
   // 2.調用 start 方法開始執(zhí)行操作
   [op start];
}

輸出結果:

image.png

可以看到:在沒有使用NSOperationQueue扩劝,在主線程中單獨使用 NSBlockOperation執(zhí)行一個操作的情況下,操作是在當前線程執(zhí)行的职辅,并沒有開啟新線程

NSBlockOperation提供了一個方法addExecutionBlock:棒呛,通過 addExecutionBlock: 方法就可以為NSBlockOperation添加額外的操作。這些操作(包括 blockOperationWithBlock 中的操作)可以在不同的線程中并發(fā)執(zhí)行域携。只有當所有相關的操作已經(jīng)完成執(zhí)行時簇秒,才視為完成。
效果如下:

/**
 * 使用子類 NSBlockOperation
 * 調用方法 AddExecutionBlock:
 */
- (void)addExecutionBlock {
    
    // 1.創(chuàng)建 NSBlockOperation 對象
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op-->%@", [NSThread currentThread]); // 打印當前線程
    }];
    
    // 2.添加額外的操作
    // 添加多個addExecutionBlock
    for (int i = 0; i < 6; i++) {
        [op addExecutionBlock:^{
        NSLog(@"op%d-->%@", i, [NSThread currentThread]); // 打印當前線程
        }];
    }
    
    // 3.調用 start 方法開始執(zhí)行操作
    [op start];
}

輸出結果:

image.png

可以看出:使用子類NSBlockOperation秀鞭,并調用方法addExecutionBlock:的情況下blockOperationWithBlock:方法中的操作 和addExecutionBlock:中的操作是在不同的線程中異步執(zhí)行的趋观。blockOperationWithBlock:中的操作也可能會在其他線程(非當前線程或主線程)中執(zhí)行
注意:ddExecutionBlock: 方法必須在start()方法之前執(zhí)行,否則就會報錯

二锋边、NSOperationQueue (隊列):

  • 調用一個NSOperation對象的start()方法來啟動這個任務皱坛,這個默認是同步執(zhí)行的。就算是addExecutionBlock:方法豆巨,也會在當前線程和其他線程中執(zhí)行麸恍,也就是說還是會占用當前線程(從上面打印結果可以看出來)。如果不想這個任務在主線程中執(zhí)行(代碼默認情況下都在主線程中執(zhí)行)這時候就要用到隊列NSOperationQueue 搀矫。
  • 按類型來說的話一共有兩種類型:主隊列其他隊列刻肄。只要添加到隊列瓤球,會自動調用任務的start()方法。
  • 主隊列
    主隊列是串行隊列敏弃,添加到主隊列的任務都會一個接一個地排著隊在主線程處理(添加額外操作除外addExecutionBlock)
    // 主隊列獲取方法
    NSOperationQueue *queue = [NSOperationQueue mainQueue];
    
  • 自定義隊列(非主隊列)
    添加到這種隊列中的操作卦羡,就會自動放到子線程中執(zhí)行,并包含了:串行、并發(fā)功能
    // 自定義隊列創(chuàng)建方法
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    

三绿饵、NSOperation欠肾、NSOperationQueue 使用步驟

  • NSOperation 需要配合 NSOperationQueue 來實現(xiàn)多線程。在默認情況下拟赊,NSOperation單獨使用時系統(tǒng)是同步執(zhí)行操作刺桃,配合NSOperationQueue實現(xiàn)異步執(zhí)行
  • 實現(xiàn)多線程的方法:
1.利用addOperationWithBlock添加操作到隊列
/**
 * 使用 addOperationWithBlock: 將操作加入到操作隊列中
 */
- (void)addOperationWithBlock {
    // 1.創(chuàng)建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 2.使用 addOperationWithBlock: 添加操作到隊列中
    for (int i = 0; i < 5; i++) {
        [queue addOperationWithBlock:^{
            NSLog(@"op%d-->%@", i, [NSThread currentThread]);
        }];
    }
}

打印結果:
image.png

可以看到:此時是開辟了多線程,進行并發(fā)執(zhí)行

2.利用addOperation添加操作到隊列
- (void)operationQueue {
    // 1.創(chuàng)建隊列:創(chuàng)建NSOperationQueue對象
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 2.創(chuàng)建操作:將需要執(zhí)行的操作封裝到NSOperation對象中
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op1-->%@", [NSThread currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op2-->%@", [NSThread currentThread]);
    }];
    
    // 3.將操作加入到隊列中:將NSOperation對象添加到NSOperationQueue對象中
    [queue addOperation:op1];
    [queue addOperation:op2];

    // 可以對以上操作進行順序執(zhí)行:
    // 讓op2 依賴于 op1吸祟,則先執(zhí)行op1瑟慈,在執(zhí)行op2
//    [op2 addDependency:op1];
    
    // 讓op1 依賴于 op2,則先執(zhí)行op2屋匕,在執(zhí)行op1
    [op1 addDependency:op2];
    
    NSLog(@"queue-->%@", [NSThread currentThread]);
}

打印結果:
image.png

可以看到:此時是開辟了多線程葛碧,進行并發(fā)執(zhí)行

四、NSOperation过吻、NSOperationQueue的一些其他功能

1.NSOperationQueue 控制串行執(zhí)行进泼、并發(fā)執(zhí)行

通過控制最大并發(fā)操作數(shù):maxConcurrentOperationCount可以達到串行或并發(fā)的效果。
這里maxConcurrentOperationCount控制的不是并發(fā)線程的數(shù)量纤虽,而是一個隊列中同時能并發(fā)執(zhí)行的最大操作數(shù)乳绕。而且一個操作也并非只能在一個線程中運行。

- (void)setMaxConcurrentOperationCount {
    // 1.創(chuàng)建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 2.設置最大并發(fā)操作數(shù)
    queue.maxConcurrentOperationCount = 1;
//    queue.maxConcurrentOperationCount = 2;
    // 3.添加操作
    for (int i = 0; i < 5; i++) {
        [queue addOperationWithBlock:^{
            for (int j = 0; j < 2; j++) {
                NSLog(@"queue%d-->%@", i, [NSThread currentThread]);
            }
        }];
    }
}
  • 最大并發(fā)數(shù)maxConcurrentOperationCount = 1的時候:操作是按順序串行執(zhí)行的廓推,并且一個操作完成之后刷袍,下一個操作才開始執(zhí)行
    image.png
  • 最大并發(fā)數(shù)maxConcurrentOperationCount = 2的時候:操作是并發(fā)執(zhí)行的,可以同時執(zhí)行兩個操作樊展。但是開啟線程數(shù)量是由系統(tǒng)決定的呻纹,不需要我們來管理
    image.png
2.NSOperation 操作依賴 (addDependency:)

控制操作之間的執(zhí)行先后順序,通過NSOperation操作的依賴進行設置专缠。

  • 設置操作依賴的幾個方法:
// 添加依賴雷酪,使當前操作依賴于操作 op 操作完成后執(zhí)行
- (void)addDependency:(NSOperation *)op;
// 移除依賴,取消當前操作對操作 op 操作的依賴
- (void)removeDependency:(NSOperation *)op;
// 在當前操作開始執(zhí)行之前完成執(zhí)行的所有操作對象數(shù)組
@property (readonly, copy) NSArray<NSOperation *> *dependencies;
  • 設置順序涝婉,添加依賴使用方法:
- (void)operationAddDependency {
    // 1.創(chuàng)建隊列:創(chuàng)建NSOperationQueue對象
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 2.創(chuàng)建操作:將需要執(zhí)行的操作封裝到NSOperation對象中
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op1-->%@", [NSThread currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op2-->%@", [NSThread currentThread]);
    }];
    
    // 3.將操作加入到隊列中:將NSOperation對象添加到NSOperationQueue對象中
    [queue addOperation:op1];
    [queue addOperation:op2];

    // 可以對以上操作進行順序執(zhí)行:
    // 讓op2 依賴于 op1哥力,則先執(zhí)行op1,在執(zhí)行op2
//    [op2 addDependency:op1];
    
    // 讓op1 依賴于 op2墩弯,則先執(zhí)行op2吩跋,在執(zhí)行op1
    [op1 addDependency:op2];
    
    NSLog(@"queue-->%@", [NSThread currentThread]);
}
3.NSOperation 優(yōu)先級

NSOperation 提供了queuePriority(優(yōu)先級)屬性,queuePriority屬性適用于同一操作隊列中的操作渔工,不適用于不同操作隊列中的操作锌钮。默認情況下,所有新創(chuàng)建的操作對象優(yōu)先級都是NSOperationQueuePriorityNormal引矩。但是我們可以通過setQueuePriority:方法來改變當前操作在同一隊列中的執(zhí)行優(yōu)先級
優(yōu)先級不能取代依賴關系

// 優(yōu)先級枚舉值
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};
4.暫停和恢復以及取消
  • 設置暫停和恢復
    suspended設置為YES表示暫停梁丘,suspended設置為NO表示恢復
    暫停表示不繼續(xù)執(zhí)行隊列中的下一個任務侵浸,暫停操作是可以恢復的
  • 取消隊列里面的所有操作
    取消之后,當前正在執(zhí)行的操作的下一個操作將不再執(zhí)行氛谜,而且永遠都不在執(zhí)行掏觉,就像后面的所有任務都從隊列里面移除了一樣
    取消操作是不可以恢復的
  • 查看否事已經(jīng)取消:isCancelled
    蘋果官方建議,每當執(zhí)行完一次耗時操作之后值漫,就查看一下當前隊列是否為取消狀態(tài)澳腹,如果是,那么就直接退出
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末惭嚣,一起剝皮案震驚了整個濱河市遵湖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌晚吞,老刑警劉巖延旧,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異槽地,居然都是意外死亡迁沫,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門捌蚊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來集畅,“玉大人,你說我怎么就攤上這事缅糟⊥χ牵” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵窗宦,是天一觀的道長赦颇。 經(jīng)常有香客問我,道長赴涵,這世上最難降的妖魔是什么价捧? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任梧奢,我火速辦了婚禮,結果婚禮上撮躁,老公的妹妹穿的比我還像新娘植榕。我一直安慰自己烟号,他們只是感情好立美,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布癣籽。 她就那樣靜靜地躺著,像睡著了一般程拭。 火紅的嫁衣襯著肌膚如雪哄陶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天哺壶,我揣著相機與錄音屋吨,去河邊找鬼。 笑死山宾,一個胖子當著我的面吹牛至扰,可吹牛的內容都是我干的。 我是一名探鬼主播资锰,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼敢课,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了绷杜?” 一聲冷哼從身側響起直秆,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鞭盟,沒想到半個月后圾结,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡齿诉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年筝野,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粤剧。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡歇竟,死狀恐怖,靈堂內的尸體忽然破棺而出抵恋,到底是詐尸還是另有隱情焕议,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布弧关,位于F島的核電站盅安,受9級特大地震影響,放射性物質發(fā)生泄漏梯醒。R本人自食惡果不足惜宽堆,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望茸习。 院中可真熱鬧畜隶,春花似錦、人聲如沸号胚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽猫胁。三九已至箱亿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間弃秆,已是汗流浹背届惋。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工髓帽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人脑豹。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓郑藏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瘩欺。 傳聞我的和親對象是個殘疾皇子必盖,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容

  • 不知不覺又是這個點了,作死的趕腳俱饿! 不知道是不是換了工作環(huán)境的因素歌粥,這兩天總覺得心情很好。加上昨天在靖江工作的堂姐...
    Lily88901閱讀 103評論 0 0
  • 公元2119年 糟糕的天氣和動蕩的社會環(huán)境讓整個地球浮躁不安拍埠,科技已經(jīng)迅速地發(fā)展到任何人無法叫停的程度了失驶,那一行...
    紫式部m閱讀 335評論 0 1
  • 昨天發(fā)的簡書文章被鎖定了,這是第二次被鎖定械拍,也不知道是哪個敏感詞起了重要作用突勇。昨天把情緒刻度表打印了兩份,對照一看...
    任重道遠_99閱讀 349評論 0 4
  • 感恩無上智者先驅坷虑,為我們?yōu)⑾轮腔鄹柿丶撞觯尦磺袩o明業(yè)障,滋養(yǎng)吾身心迄损。 感恩父母給予我今生暇滿的人身定躏,使我得以修行?...
    涵涵_a55c閱讀 106評論 0 0