iOS多線程之NSOperation铜涉、NSOperationQueue的詳細(xì)講解

  • 本作者在寫這篇文章之前很詳細(xì)的寫了一篇關(guān)于ios中GCD的講解,這兩篇文章可以對(duì)比著看证膨,地址為:http://www.reibang.com/p/94affa011185,也可以去我的文章列表中查找鼓黔。

1.什么是NSOperation:

NSOperation和NSOperationQueue 是蘋果提供給我們的一套多線程解決方案央勒。實(shí)際上 NSOperation和NSOperationQueue 是基于 GCD 更高一層的封裝不见,完全面向?qū)ο蟆5潜?GCD 更簡(jiǎn)單易用订歪、代碼可讀性也更高脖祈。

2.NSOperation的子類

NSOperation是一個(gè)抽象類,并不具備封裝操作的能力刷晋,必須使用它的子類盖高,使用NSOperation的子類的方式有三種:

  1. NSInvocationOperation以函數(shù)的方式封裝任務(wù)
  2. NSBlockOperation以Block塊的方式封裝任務(wù)
  3. 自定義子類繼承NSOperation,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法眼虱。

3.NSOperation喻奥、NSOperationQueue 操作和操作隊(duì)列

既然是基于 GCD 的更高一層的封裝。那么捏悬,GCD 中的一些概念同樣適用于 NSOperation撞蚕、NSOperationQueue。在 NSOperation过牙、NSOperationQueue 中也有類似的任務(wù)(操作)和隊(duì)列(操作隊(duì)列)的概念甥厦。

3.1 操作(Operation):
  • 執(zhí)行操作的意思,換句話說就是你在線程中執(zhí)行的那段代碼寇钉。
  • 在 GCD 中是放在 block 中的刀疙。在 NSOperation 中,我們使用 NSOperation 子類 NSInvocationOperation扫倡、NSBlockOperation谦秧,或者自定義子類來封裝操作。
3.2 操作隊(duì)列(Operation Queues):
  • 這里的隊(duì)列指操作隊(duì)列撵溃,即用來存放操作的隊(duì)列疚鲤。不同于 GCD 中的調(diào)度隊(duì)列 FIFO(先進(jìn)先出)的原則。NSOperationQueue 對(duì)于添加到隊(duì)列中的操作缘挑,首先進(jìn)入準(zhǔn)備就緒的狀態(tài)(就緒狀態(tài)取決于操作之間的依賴關(guān)系)集歇,然后進(jìn)入就緒狀態(tài)的操作的開始執(zhí)行順序(非結(jié)束執(zhí)行順序)由操作之間相對(duì)的優(yōu)先級(jí)決定(優(yōu)先級(jí)是操作對(duì)象自身的屬性)。
  • 操作隊(duì)列通過設(shè)置最大并發(fā)操作數(shù)(maxConcurrentOperationCount)來控制并發(fā)语淘、串行鬼悠。
  • NSOperationQueue 為我們提供了兩種不同類型的隊(duì)列:主隊(duì)列和自定義隊(duì)列。其中自定義隊(duì)列默認(rèn)為并發(fā)隊(duì)列亏娜,但是可以通過控制來變成一個(gè)串行隊(duì)列,主隊(duì)列為串行隊(duì)列蹬挺,運(yùn)行在主線程之上维贺,而自定義隊(duì)列在后臺(tái)執(zhí)行。

4.NSOperation和NSOperationQueue實(shí)現(xiàn)多線程的具體步驟

  1. 先將需要執(zhí)行的操作封裝到一個(gè)NSOperation中
  2. 然后將NSOperation對(duì)象添加到NSOperationQueue中
  3. 系統(tǒng)會(huì)自動(dòng)將NSOperationQueue中的NSOperation取出來
  4. 將取出來的NSOperation封裝的操作放到一條新線程中執(zhí)行
//簡(jiǎn)單添加任務(wù)
/*
 輸入結(jié)果
 2019-03-04 15:44:05.880348+0800 SmallProgram[4075:178369] 1---<NSThread: 0x600000ece7c0>{number = 1, name = main}
 2019-03-04 15:44:05.880541+0800 SmallProgram[4075:178369] 2---<NSThread: 0x600000ece7c0>{number = 1, name = main}
 2019-03-04 15:44:05.880711+0800 SmallProgram[4075:178369] 3---<NSThread: 0x600000ece7c0>{number = 1, name = main}
 2019-03-04 15:44:05.880736+0800 SmallProgram[4075:178504] 4---<NSThread: 0x600000e68b00>{number = 7, name = (null)}
 2019-03-04 15:44:05.880736+0800 SmallProgram[4075:178482] 5---<NSThread: 0x600000e68140>{number = 8, name = (null)}
 */
-(void)blockOperation{
    //01 --封裝操作對(duì)象
    NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)01
        NSLog(@"1---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)02
        NSLog(@"2---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)03
        NSLog(@"3---%@",[NSThread currentThread]);
    }];
    //追加任務(wù)
    //當(dāng)一個(gè)操作中的任務(wù)數(shù)量大于1的時(shí)候巴帮,就會(huì)開啟子線程和當(dāng)前線程一起執(zhí)行任務(wù)
    [op3 addExecutionBlock:^{
        NSLog(@"4---%@",[NSThread currentThread]);
    }];
    [op3 addExecutionBlock:^{
        NSLog(@"5---%@",[NSThread currentThread]);
    }];
    //02--執(zhí)行操作
    [op1 start];
    [op2 start];
    [op3 start];
    
}

5.操作隊(duì)列NSOperationQueue的基本使用

-(void)blockOperationQueue{
    
    //01 --創(chuàng)建隊(duì)列
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    //02 --封裝操作對(duì)象
    NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)01
        NSLog(@"1---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)02
        NSLog(@"2---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)03
        NSLog(@"3---%@",[NSThread currentThread]);
    }];
    //03 --把操作添加到隊(duì)列中
    [queue addOperation:op1]; //該方法內(nèi)部會(huì)自動(dòng)調(diào)用start方法執(zhí)行任務(wù)
    [queue addOperation:op2];
    [queue addOperation:op3];
    
    /*
    //簡(jiǎn)便方法:該方法n首先會(huì)吧block中任務(wù)封裝成一個(gè)操作溯泣,然后把該操作直接添加到隊(duì)列中
    [queue addOperationWithBlock:^{
        NSLog(@"4---%@",[NSThread currentThread]);
    }];
    */
}

6.設(shè)置maxConcurrentOperationCount來改為串行隊(duì)列

-(void)changeSerialQueue{
    
    //01 --創(chuàng)建隊(duì)列
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    //02 --封裝操作對(duì)象
    NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)01
        NSLog(@"1---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)02
        NSLog(@"2---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)03
        NSLog(@"3---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op4 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)03
        NSLog(@"4---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op5 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)03
        NSLog(@"5---%@",[NSThread currentThread]);
    }];
    //設(shè)置最大并發(fā)數(shù)--同一時(shí)間最多有多少調(diào)線程執(zhí)行
    //maxConcurrentOperationCount ==0 不執(zhí)行任務(wù)
    //maxConcurrentOperationCount默認(rèn)為-1虐秋,指一個(gè)最大的值
    queue.maxConcurrentOperationCount = 1;
    //03 --把操作添加到隊(duì)列中
    [queue addOperation:op1]; //該方法內(nèi)部會(huì)自動(dòng)調(diào)用start方法執(zhí)行任務(wù)
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue addOperation:op5];

}

7.操作隊(duì)列的暫停、回復(fù)和取消功能

    //暫停
    //只能暫停當(dāng)前操作后面的操作垃沦,當(dāng)前操作不可分割必須執(zhí)行完畢
    [queue setSuspended:YES];
    //恢復(fù)
    [queue setSuspended:NO];
    //取消
    //智能取消隊(duì)列中處理等待狀態(tài)的操作
    [queue cancelAllOperations];

8.自定義操作

//.h文件
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface MYNSOperation : NSOperation

@end

NS_ASSUME_NONNULL_END

//.m文件
#import "MYNSOperation.h"

@implementation MYNSOperation

//重寫main方法來告訴自定義的操作任務(wù)是什么
//好處及其使用:比如工程中多次利用到圖片下載客给,就可以將下載功能放到main方法中,以后調(diào)用就直接alloc就可以了
-(void)main{
    if(self.isCancelled){
        return;
    }
    //執(zhí)行的任務(wù)
    for(int i=0;i<5;i++){
        NSLog(@"%d--%@",i,[NSThread currentThread]);
    }
}
@end

//使用方法
NSOperationQueue * queue = [[NSOperationQueue alloc]init];
MYNSOperation * op = [[MYNSOperation alloc]init];
[queue addOperation:op]; //內(nèi)容會(huì)調(diào)用start--->main

9.操作的依賴和監(jiān)聽

/*
 輸出結(jié)果:
 2019-03-04 17:14:04.382909+0800 SmallProgram[5396:240448] 4---<NSThread: 0x600001e9c000>{number = 3, name = (null)}
 2019-03-04 17:14:04.383237+0800 SmallProgram[5396:240448] 電影已經(jīng)下載好了肢簿,可以觀看了
 2019-03-04 17:14:04.383261+0800 SmallProgram[5396:240470] 3---<NSThread: 0x600001e59b80>{number = 9, name = (null)}
 2019-03-04 17:14:04.383440+0800 SmallProgram[5396:240470] 2---<NSThread: 0x600001e59b80>{number = 9, name = (null)}
 2019-03-04 17:14:04.383712+0800 SmallProgram[5396:240448] 1---<NSThread: 0x600001e9c000>{number = 3, name = (null)}
 2019-03-04 17:14:04.383955+0800 SmallProgram[5396:240448] 5---<NSThread: 0x600001e9c000>{number = 3, name = (null)}
 */
-(void)relyOn{
    
    //01 --創(chuàng)建隊(duì)列
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    NSOperationQueue * queue2 = [[NSOperationQueue alloc] init];
    //02 --封裝操作對(duì)象
    NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)01
        NSLog(@"1---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)02
        NSLog(@"2---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)03
        NSLog(@"3---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op4 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)03
        NSLog(@"4---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op5 = [NSBlockOperation blockOperationWithBlock:^{
        //任務(wù)03
        NSLog(@"5---%@",[NSThread currentThread]);
    }];
    
    //監(jiān)聽任務(wù)執(zhí)行完畢
    op4.completionBlock = ^{
        NSLog(@"電影已經(jīng)下載好了靶剑,可以觀看了");
    };
    
    //03設(shè)置依賴4-->3-->2-->1-->5
    //警告:不能設(shè)置循環(huán)依賴,否則死鎖
    [op5 addDependency:op1];
    [op1 addDependency:op2];
    [op2 addDependency:op3];
    [op3 addDependency:op4];
    
    //04 --把操作添加到隊(duì)列中
    [queue addOperation:op1]; //該方法內(nèi)部會(huì)自動(dòng)調(diào)用start方法執(zhí)行任務(wù)
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue2 addOperation:op5];
}

10.操作隊(duì)列實(shí)現(xiàn)線程間通信(模仿子線程下載圖片池充,然后主線程刷新UI)

/*
 輸出結(jié)果:
 下載中--<NSThread: 0x600001328bc0>{number = 8, name = (null)}
 2019-03-04 17:24:28.905424+0800 SmallProgram[5558:245680] 刷新UI--<NSThread: 0x600001389840>{number = 1, name = main}
 */
-(void)download{
    //01 --創(chuàng)建隊(duì)列
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    //02 --封裝操作對(duì)象
    NSBlockOperation * download = [NSBlockOperation blockOperationWithBlock:^{
        //01--下載
        NSLog(@"下載中--%@",[NSThread currentThread]);
        //02--UI
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"刷新UI--%@",[NSThread currentThread]);
        }];
        
    }];
    [queue addOperation:download];
}

11.NSOperation桩引、NSOperationQueue 線程安全(模仿賣火車票)

- (void)ticketStatusSave {
    
    self.ticketCount = 100; //模擬總共100張票
    self.lock = [[NSLock alloc] init]; //初始化鎖
    
    // 01.創(chuàng)建 queue1,模擬窗口1
    NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
    queue1.maxConcurrentOperationCount = 1;
    
    // 02.創(chuàng)建 queue2,模擬窗口2
    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
    queue2.maxConcurrentOperationCount = 1;
    
    // 03.添加到隊(duì)列,然后開始賣票
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        [self saleTicketSafe];
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        [self saleTicketSafe];
    }];
    
    [queue1 addOperation:op1];
    [queue2 addOperation:op2];
}

/**
 * 售賣火車票(線程安全)
 */
- (void)saleTicketSafe {
    while (1) {
        
        // 加鎖
        [self.lock lock];
        
        if (self.ticketCount > 0) {
            //如果還有票收夸,繼續(xù)售賣
            self.ticketCount--;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余票數(shù):%zd 窗口:%@", self.ticketCount, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:0.2];
        }
        // 解鎖
        [self.lock unlock];
        if (self.ticketCount <= 0) {
            NSLog(@"所有火車票均已售完");
            break;
        }
    }
}

12.GCD和NSOperation的比較

12.1 GCD:
  • GCD是純C語(yǔ)言的API坑匠,而操作隊(duì)列則是Object-C對(duì)象。
  • 在GCD中卧惜,任務(wù)塊(Block)來表示厘灼,而塊是個(gè)輕量級(jí)的數(shù)據(jù)結(jié)構(gòu),相反操作隊(duì)列中的NSOperation則是個(gè)更加重量級(jí)的Object-C對(duì)象咽瓷。
  • 具體該使用哪種還得具體情況分析设凹。
12.2 NSOperation和NSOperationQueue的優(yōu)勢(shì):
  • NSOperation和NSOperationQueue可以方便的調(diào)用cancel方法來取消某個(gè)操作,而GCD中的任務(wù)是無法被取消的(安排好任務(wù)之后就不管了)忱详。
  • NSOperation可以方便的指定操作間的依賴關(guān)系围来。
  • NSOperation可以通過KVO提供對(duì)NSOperation對(duì)象的精細(xì)控制(如監(jiān)聽當(dāng)前操作是否被取消或者是否已經(jīng)完成)
  • NSOperation可以方便的設(shè)置操作的優(yōu)先級(jí)(qualityOfService),操作優(yōu)先級(jí)表示此操作與隊(duì)列中其它操作之間的優(yōu)先關(guān)系匈睁,優(yōu)先級(jí)高德操作先執(zhí)行监透。
  • 通過自定義的NSOperation的子類可以實(shí)現(xiàn)操作重用。

結(jié)尾

本文參考:
iOS 多線程:『NSOperation航唆、NSOperationQueue』詳盡總結(jié)http://www.reibang.com/p/4b1d77054b35

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末胀蛮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子糯钙,更是在濱河造成了極大的恐慌粪狼,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件任岸,死亡現(xiàn)場(chǎng)離奇詭異再榄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)享潜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門困鸥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人剑按,你說我怎么就攤上這事疾就±绞酰” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵猬腰,是天一觀的道長(zhǎng)鸟废。 經(jīng)常有香客問我,道長(zhǎng)姑荷,這世上最難降的妖魔是什么盒延? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮厢拭,結(jié)果婚禮上兰英,老公的妹妹穿的比我還像新娘。我一直安慰自己供鸠,他們只是感情好畦贸,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著楞捂,像睡著了一般薄坏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寨闹,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天胶坠,我揣著相機(jī)與錄音,去河邊找鬼繁堡。 笑死沈善,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的椭蹄。 我是一名探鬼主播闻牡,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼绳矩!你這毒婦竟也來了罩润?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤翼馆,失蹤者是張志新(化名)和其女友劉穎割以,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體应媚,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡严沥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了中姜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片消玄。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出莱找,到底是詐尸還是另有隱情,我是刑警寧澤嗜桌,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布奥溺,位于F島的核電站,受9級(jí)特大地震影響骨宠,放射性物質(zhì)發(fā)生泄漏浮定。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一层亿、第九天 我趴在偏房一處隱蔽的房頂上張望桦卒。 院中可真熱鬧,春花似錦匿又、人聲如沸方灾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)裕偿。三九已至,卻和暖如春痛单,著一層夾襖步出監(jiān)牢的瞬間嘿棘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工旭绒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鸟妙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓挥吵,卻偏偏與公主長(zhǎng)得像重父,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蔫劣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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