OC高級(jí)-NSOperation 和 NSOperationQueue

實(shí)現(xiàn)多線程的具體步驟

  • 先將需要執(zhí)行的操作封裝到一個(gè)NSOperation對(duì)象
  • 然后將NSOperation對(duì)象添加到NSOperationQueue
  • 系統(tǒng)自動(dòng)從NSOperationQueue中取出NSOperation對(duì)象放到適合的線程中執(zhí)行

NSOperation

  • NSOperation是個(gè)抽象類,并不具備封裝操作的能力
  • 只能通過它的子類來使用它的功能
  • 有三種方式來使用NSOperation
    • NSInvocationOperation
    • NSBlockOperation
    • 自定義繼承自NSOperation的操作

NSInvocationOperation

  • 創(chuàng)建NSInvocationOperation對(duì)象馋记,通過對(duì)象方法創(chuàng)建
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
  • 必須手動(dòng)調(diào)用start方法才開始執(zhí)行操作
  • 默認(rèn)情況下,調(diào)用了start方法后并不會(huì)開一條新線程去執(zhí)行操作村象,而是在當(dāng)前線程中同步執(zhí)行操作
- (void)invocationOperation
{
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

    // 調(diào)用start方法
    [op start];
}

- (void)run
{
    NSLog(@"------%@", [NSThread currentThread]);
}

NSBlockOperation

  • 創(chuàng)建NSBlockOperation對(duì)象榨崩,通過類方法創(chuàng)建
+ (id)blockOperationWithBlock:(void (^)(void))block;
  • 通過addExecutionBlock:方法添加多個(gè)操作
- (void)addExecutionBlock:(void (^)(void))block;
  • 必須手動(dòng)調(diào)用start方法才開始執(zhí)行操作
- (void)blockOperation
{
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^
    {
        // 這個(gè)任務(wù)是在【主線程】
        NSLog(@"下載1------%@", [NSThread currentThread]);
    }];

    // 添加額外的任務(wù)(在子線程執(zhí)行)
    [op addExecutionBlock:^{
        NSLog(@"下載2------%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"下載3------%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"下載4------%@", [NSThread currentThread]);
    }];

    [op start];
}
  • 第一個(gè)任務(wù)是在當(dāng)前線程中執(zhí)行削解,后續(xù)的任務(wù)才會(huì)在子線程中執(zhí)行
  • 只有當(dāng)封裝的操作數(shù)> 1把夸,才會(huì)異步執(zhí)行操作

自定義繼承自NSOperation的操作

  • 重寫- (void)main方法,在里面實(shí)現(xiàn)想執(zhí)行的任務(wù)邏輯
  • 自己創(chuàng)建自動(dòng)釋放池锋华,如果操作是在子線程中完成嗡官,那么子線程中是無法訪問主線程的自動(dòng)釋放池,所以自定Operation需要在外層添加@autoreleasepool{ }
  • 合理布局供置,在適當(dāng)位置調(diào)用isCancelled屬性檢測(cè)操作是否已被取消谨湘,及時(shí)中斷操作
- (void)main
{
    @autoreleasepool
    {
        for (NSInteger i = 0; i < 1000; i++)
        {
            NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
        }

        // 由于任務(wù)一旦開始執(zhí)行就沒辦法停止下來
        // 蘋果官方建議,如果是自定義的Operation芥丧,而且內(nèi)部的執(zhí)行邏輯很耗時(shí)
        // 如果外面調(diào)用了cancel方法,可以通過在一段耗時(shí)邏輯后調(diào)用一次isCancelled方法判斷操作是否已經(jīng)取消坊罢,以用來中斷任務(wù)的執(zhí)行
        
        // 合理布局此方法
        if (self.isCancelled) return;

        for (NSInteger i = 0; i< 1000; i++)
        {
            NSLog(@"download2 -%zd-- %@", i, [NSThread currentThread]);
        }

        if (self.isCancelled) return;

        for (NSInteger i = 0; i < 1000; i++)
        {
            NSLog(@"download3 -%zd-- %@", i, [NSThread currentThread]);
        }

        if (self.isCancelled) return;
    }
}

NSOperation設(shè)置依賴

  • 比如任務(wù)A執(zhí)行完后续担,才能執(zhí)行任務(wù)B,可以這么設(shè)置
// 操作B依賴于操作A
[operationB addDependency:operationA];
  • 可以在不同隊(duì)列的NSOperation之間創(chuàng)建依賴關(guān)系


    不同隊(duì)列的NSOperation之間創(chuàng)建依賴關(guān)系
  • NSOperation之間不能相互依賴活孩,比如:A依賴B物遇,B依賴A
- (void)addDependencyTest
{
    // 直接創(chuàng)建
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download1----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download2----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3----%@", [NSThread  currentThread]);
    }];

    // 設(shè)置依賴后再添加到隊(duì)列
    // 當(dāng)op1和op2都執(zhí)行完才執(zhí)行op3,但是op1和op2誰先執(zhí)行完不確定
    [op3 addDependency:op1];
    [op3 addDependency:op2];

    // 【不能】循環(huán)依賴
    //    [op3 addDependency:op1];
    //    [op1 addDependency:op3];

    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
}
  • NSOperation執(zhí)行完畢后想做一些收尾的事情憾儒,可以設(shè)置completionBlock屬性
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^
{
    NSLog(@"download----%@", [NSThread  currentThread]);
}];

// 任務(wù)執(zhí)行完畢就會(huì)執(zhí)行這個(gè)block
op.completionBlock = ^
{
    // 也是在子線程中執(zhí)行
    NSLog(@"op執(zhí)行完畢后執(zhí)行---%@", [NSThread currentThread]);
};

NSOperationQueue

  • 直接創(chuàng)建NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  • 如果將NSOperation添加到NSOperationQueue中询兴,系統(tǒng)會(huì)自動(dòng)異步執(zhí)行NSOperation中的操作
  • 本質(zhì)上NSOperationQueue內(nèi)部也是自動(dòng)調(diào)用了NSOperation的start方法來執(zhí)行任務(wù)
  • 同時(shí)包含了串行 和 并行功能

添加操作到NSOperationQueue中方法

- (void)addOperation:(NSOperation *)op;

// 推薦使用此種方式添加任務(wù)
- (void)addOperationWithBlock:(void (^)(void))block;

最大并發(fā)數(shù)

  • 同時(shí)執(zhí)行的最大任務(wù)數(shù)
  • 當(dāng)最大并發(fā)數(shù)為1的時(shí)候就是串行隊(duì)列,但所有任務(wù)不一定同一個(gè)線程上執(zhí)行完起趾,可能在多個(gè)線程上執(zhí)行诗舰,但一定是按順序執(zhí)行
  • 最大并發(fā)數(shù)設(shè)置方式
// 調(diào)用示例
queue.maxConcurrentOperationCount = 1;

暫停和恢復(fù)隊(duì)列:suspended

  • 暫停后還未執(zhí)行的任務(wù)將不會(huì)被執(zhí)行,但是已經(jīng)開始的任務(wù)是不能立即停下來了
// 調(diào)用示例
queue.suspended = !self.queue.suspended;
  • suspended的應(yīng)用場(chǎng)景:隊(duì)列正在執(zhí)行一些耗時(shí)操作训裆,當(dāng)用戶執(zhí)行滾動(dòng)操作時(shí)眶根,為了用戶體驗(yàn)可以調(diào)用這個(gè)屬性先讓隊(duì)列中的任務(wù)暫停執(zhí)行,以用來提高用戶體驗(yàn)

取消隊(duì)列的所有操作:cancelAllOperations

  • 取消隊(duì)列中的所有任務(wù)边琉,會(huì)將隊(duì)列中的任務(wù)全部移除
  • 這個(gè)方法內(nèi)部其實(shí)是調(diào)用了NSOperation中的cancel方法
  • 所以可以通過NSOperation中的cancel方法來取消單個(gè)操作
  • 只能取消還未執(zhí)行的任務(wù)属百,已經(jīng)開始的任務(wù)不能取消,除非任務(wù)中主動(dòng)判斷了是否取消操作queue.isCancelled來中斷操作
// 調(diào)用示例
[queue cancelAllOperations];

線程間通信

- (void)test
{
    [[[NSOperationQueue alloc] init] addOperationWithBlock:^
    {
        // 圖片的網(wǎng)絡(luò)路徑
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];

        // 加載圖片
        NSData *data = [NSData dataWithContentsOfURL:url];

        // 生成圖片
        UIImage *image = [UIImage imageWithData:data];

        // 回到主線程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^
        {
            self.imageView.image = image;
        }];
    }];
}

GCD的隊(duì)列和NSOperationQueue隊(duì)列對(duì)比

GCD的隊(duì)列

  • 并發(fā)隊(duì)列
    • 自己創(chuàng)建的(DISPATCH_QUEUE_CONCURRENT
    • 全局隊(duì)列
  • 串行隊(duì)列
    • 自己創(chuàng)建的(DISPATCH_QUEUE_SERIAL or NULL
    • 主隊(duì)列(特殊的串行隊(duì)列)

NSOperationQueue隊(duì)列類型

  • 主隊(duì)列
    • [NSOperationQueue mainQueue]
    • 凡是添加到主隊(duì)列中的任務(wù)(NSOperation)变姨,都會(huì)放到主線程中執(zhí)行
  • 非主隊(duì)列(其他隊(duì)列)
    • 創(chuàng)建:[[NSOperationQueue alloc] init]
    • 同時(shí)包含了串行 和 并行功能
    • 當(dāng)設(shè)置最大并發(fā)數(shù)為queue.maxConcurrentOperationCount = 1時(shí)就是串行隊(duì)列
    • 添加到隊(duì)列中的操作族扰,會(huì)自動(dòng)放到適合的線程中執(zhí)行
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市定欧,隨后出現(xiàn)的幾起案子渔呵,更是在濱河造成了極大的恐慌,老刑警劉巖忧额,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厘肮,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡睦番,警方通過查閱死者的電腦和手機(jī)类茂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門耍属,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人巩检,你說我怎么就攤上這事厚骗。” “怎么了兢哭?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵领舰,是天一觀的道長。 經(jīng)常有香客問我迟螺,道長冲秽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任矩父,我火速辦了婚禮锉桑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘窍株。我一直安慰自己民轴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布球订。 她就那樣靜靜地躺著后裸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪冒滩。 梳的紋絲不亂的頭發(fā)上微驶,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音旦部,去河邊找鬼祈搜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛士八,可吹牛的內(nèi)容都是我干的容燕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼婚度,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蘸秘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蝗茁,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤醋虏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后哮翘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颈嚼,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年饭寺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了阻课。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叫挟。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖限煞,靈堂內(nèi)的尸體忽然破棺而出抹恳,到底是詐尸還是另有隱情,我是刑警寧澤署驻,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布奋献,位于F島的核電站,受9級(jí)特大地震影響旺上,放射性物質(zhì)發(fā)生泄漏瓶蚂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一抚官、第九天 我趴在偏房一處隱蔽的房頂上張望扬跋。 院中可真熱鬧,春花似錦凌节、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至垒棋,卻和暖如春卒煞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叼架。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來泰國打工畔裕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乖订。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓扮饶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親乍构。 傳聞我的和親對(duì)象是個(gè)殘疾皇子甜无,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

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