iOS-底層原理 27:GCD 之 NSThread & GCD & NSOperation

iOS 底層原理 文章匯總

本文的主要目的是介紹 NSThread赌躺、GCDNSOperation常見的使用方式

NSthread

NSthread是蘋果官方提供面向?qū)ο蟮木€程操作技術(shù)峰档,是對(duì)thread的上層封裝,比較偏向于底層寨昙。簡單方便讥巡,可以直接操作線程對(duì)象,使用頻率較少毅待。

創(chuàng)建線程
線程的創(chuàng)建方式主要以下三種方式

  • 通過init初始化方式創(chuàng)建

  • 通過detachNewThreadSelector構(gòu)造器方式創(chuàng)建

  • 通過performSelector...方法創(chuàng)建尚卫,主要是用于獲取主線程,以及后臺(tái)線程

//1尸红、創(chuàng)建
- (void)cjl_createNSThread{
    NSString *threadName1 = @"NSThread1";
    NSString *threadName2 = @"NSThread2";
    NSString *threadName3 = @"NSThread3";
    NSString *threadNameMain = @"NSThreadMain";
    
    //方式一:初始化方式吱涉,需要手動(dòng)啟動(dòng)
    NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:) object:threadName1];
    [thread1 start];
    
    //方式二:構(gòu)造器方式,自動(dòng)啟動(dòng)
    [NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:threadName2];
    
    //方式三:performSelector...方法創(chuàng)建
    [self performSelectorInBackground:@selector(doSomething:) withObject:threadName3];
    
    //方式四
    [self performSelectorOnMainThread:@selector(doSomething:) withObject:threadNameMain waitUntilDone:YES];
    
}
- (void)doSomething:(NSObject *)objc{
    NSLog(@"%@ - %@", objc, [NSThread currentThread]);
}

屬性

- thread.isExecuting    //線程是否在執(zhí)行
- thread.isCancelled    //線程是否被取消
- thread.isFinished     //是否完成
- thread.isMainThread   //是否是主線程
- thread.threadPriority //線程的優(yōu)先級(jí)外里,取值范圍0.0-1.0,默認(rèn)優(yōu)先級(jí)0.5怎爵,1.0表示最高優(yōu)先級(jí),優(yōu)先級(jí)高盅蝗,CPU調(diào)度的頻率高

類方法
常用的類方法有以下幾個(gè):

  • currentThread:獲取當(dāng)前線程

  • sleep...:阻塞線程

  • exit:退出線程

  • mainThread:獲取主線程

- (void)cjl_NSThreadClassMethod{
    //當(dāng)前線程
    [NSThread currentThread];
    // 如果number=1鳖链,則表示在主線程,否則是子線程
    NSLog(@"%@", [NSThread currentThread]);
    
    //阻塞休眠
    [NSThread sleepForTimeInterval:2];//休眠多久
    [NSThread sleepUntilDate:[NSDate date]];//休眠到指定時(shí)間
    
    //其他
    [NSThread exit];//退出線程
    [NSThread isMainThread];//判斷當(dāng)前線程是否為主線程
    [NSThread isMultiThreaded];//判斷當(dāng)前線程是否是多線程
    NSThread *mainThread = [NSThread mainThread];//主線程的對(duì)象
    NSLog(@"%@", mainThread);

GCD

dispatch_after

- (void)cjl_testAfter{
    /*
     dispatch_after表示在某隊(duì)列中的block延遲執(zhí)行
     應(yīng)用場(chǎng)景:在主隊(duì)列上延遲執(zhí)行一項(xiàng)任務(wù)墩莫,如viewDidload之后延遲1s芙委,提示一個(gè)alertview(是延遲加入到隊(duì)列,而不是延遲執(zhí)行)
     */
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"2s后輸出");
    });
   
}

dispatch_once

- (void)cjl_testOnce{
    /*
     dispatch_once保證在App運(yùn)行期間狂秦,block中的代碼只執(zhí)行一次
     應(yīng)用場(chǎng)景:單例灌侣、method-Swizzling
     */
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //創(chuàng)建單例、method swizzled或其他任務(wù)
        NSLog(@"創(chuàng)建單例");
    });
}

dispatch_apply

- (void)cjl_testApply{
    /*
     dispatch_apply將指定的Block追加到指定的隊(duì)列中重復(fù)執(zhí)行裂问,并等到全部的處理執(zhí)行結(jié)束——相當(dāng)于線程安全的for循環(huán)

     應(yīng)用場(chǎng)景:用來拉取網(wǎng)絡(luò)數(shù)據(jù)后提前算出各個(gè)控件的大小侧啼,防止繪制時(shí)計(jì)算,提高表單滑動(dòng)流暢性
     - 添加到串行隊(duì)列中——按序執(zhí)行
     - 添加到主隊(duì)列中——死鎖
     - 添加到并發(fā)隊(duì)列中——亂序執(zhí)行
     - 添加到全局隊(duì)列中——亂序執(zhí)行
     */
    
    dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_SERIAL);
    NSLog(@"dispatch_apply前");
    /**
         param1:重復(fù)次數(shù)
         param2:追加的隊(duì)列
         param3:執(zhí)行任務(wù)
         */
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"dispatch_apply 的線程 %zu - %@", index, [NSThread currentThread]);
    });
    NSLog(@"dispatch_apply后");
}

dispatch_group_t

有以下兩種使用方式

  • 【方式一】使用dispatch_group_async + dispatch_group_notify
- (void)cjl_testGroup1{
    /*
     dispatch_group_t:調(diào)度組將任務(wù)分組執(zhí)行堪簿,能監(jiān)聽任務(wù)組完成痊乾,并設(shè)置等待時(shí)間

     應(yīng)用場(chǎng)景:多個(gè)接口請(qǐng)求之后刷新頁面
     */
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"請(qǐng)求一完成");
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"請(qǐng)求二完成");
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"刷新頁面");
    });
}
  • 【方式二】使用dispatch_group_enter + dispatch_group_leave + dispatch_group_notify
- (void)cjl_testGroup2{
    /*
     dispatch_group_enter和dispatch_group_leave成對(duì)出現(xiàn),使進(jìn)出組的邏輯更加清晰
     */
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"請(qǐng)求一完成");
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"請(qǐng)求二完成");
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"刷新界面");
    });
}
  • 在方式二的基礎(chǔ)上增加超時(shí)dispatch_group_wait
- (void)cjl_testGroup3{
    /*
     long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout)

     group:需要等待的調(diào)度組
     timeout:等待的超時(shí)時(shí)間(即等多久)
        - 設(shè)置為DISPATCH_TIME_NOW意味著不等待直接判定調(diào)度組是否執(zhí)行完畢
        - 設(shè)置為DISPATCH_TIME_FOREVER則會(huì)阻塞當(dāng)前調(diào)度組椭更,直到調(diào)度組執(zhí)行完畢


     返回值:為long類型
        - 返回值為0——在指定時(shí)間內(nèi)調(diào)度組完成了任務(wù)
        - 返回值不為0——在指定時(shí)間內(nèi)調(diào)度組沒有按時(shí)完成任務(wù)

     */
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"請(qǐng)求一完成");
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"請(qǐng)求二完成");
        dispatch_group_leave(group);
    });
    
//    long timeout = dispatch_group_wait(group, DISPATCH_TIME_NOW);
//    long timeout = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    long timeout = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 1 *NSEC_PER_SEC));
    NSLog(@"timeout = %ld", timeout);
    if (timeout == 0) {
        NSLog(@"按時(shí)完成任務(wù)");
    }else{
        NSLog(@"超時(shí)");
    }
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"刷新界面");
    });
}

dispatch_barrier_sync & dispatch_barrier_async

柵欄函數(shù)哪审,主要有兩種使用場(chǎng)景:串行隊(duì)列、并發(fā)隊(duì)列

- (void)cjl_testBarrier{
    /*
     dispatch_barrier_sync & dispatch_barrier_async
     
     應(yīng)用場(chǎng)景:同步鎖
     
     等柵欄前追加到隊(duì)列中的任務(wù)執(zhí)行完畢后虑瀑,再將柵欄后的任務(wù)追加到隊(duì)列中湿滓。
     簡而言之畏腕,就是先執(zhí)行柵欄前任務(wù),再執(zhí)行柵欄任務(wù)茉稠,最后執(zhí)行柵欄后任務(wù)
     
     - dispatch_barrier_async:前面的任務(wù)執(zhí)行完畢才會(huì)來到這里
     - dispatch_barrier_sync:作用相同描馅,但是這個(gè)會(huì)堵塞線程,影響后面的任務(wù)執(zhí)行
    
     - dispatch_barrier_async可以控制隊(duì)列中任務(wù)的執(zhí)行順序而线,
     - 而dispatch_barrier_sync不僅阻塞了隊(duì)列的執(zhí)行铭污,也阻塞了線程的執(zhí)行(盡量少用)
     */
    
    [self cjl_testBarrier1];
    [self cjl_testBarrier2];
}
- (void)cjl_testBarrier1{
    //串行隊(duì)列使用柵欄函數(shù)
    
    dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_SERIAL);
    
    NSLog(@"開始 - %@", [NSThread currentThread]);
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"延遲2s的任務(wù)1 - %@", [NSThread currentThread]);
    });
    NSLog(@"第一次結(jié)束 - %@", [NSThread currentThread]);
    
    //柵欄函數(shù)的作用是將隊(duì)列中的任務(wù)進(jìn)行分組,所以我們只要關(guān)注任務(wù)1膀篮、任務(wù)2
    dispatch_barrier_async(queue, ^{
        NSLog(@"------------柵欄任務(wù)------------%@", [NSThread currentThread]);
    });
    NSLog(@"柵欄結(jié)束 - %@", [NSThread currentThread]);
    
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"延遲2s的任務(wù)2 - %@", [NSThread currentThread]);
    });
    NSLog(@"第二次結(jié)束 - %@", [NSThread currentThread]);
}
- (void)cjl_testBarrier2{
    //并發(fā)隊(duì)列使用柵欄函數(shù)
    
    dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_CONCURRENT);
    
    NSLog(@"開始 - %@", [NSThread currentThread]);
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"延遲2s的任務(wù)1 - %@", [NSThread currentThread]);
    });
    NSLog(@"第一次結(jié)束 - %@", [NSThread currentThread]);
    
    //由于并發(fā)隊(duì)列異步執(zhí)行任務(wù)是亂序執(zhí)行完畢的嘹狞,所以使用柵欄函數(shù)可以很好的控制隊(duì)列內(nèi)任務(wù)執(zhí)行的順序
    dispatch_barrier_async(queue, ^{
        NSLog(@"------------柵欄任務(wù)------------%@", [NSThread currentThread]);
    });
    NSLog(@"柵欄結(jié)束 - %@", [NSThread currentThread]);
    
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"延遲2s的任務(wù)2 - %@", [NSThread currentThread]);
    });
    NSLog(@"第二次結(jié)束 - %@", [NSThread currentThread]);
}

dispatch_semaphore_t

信號(hào)量主要用作同步鎖,用于控制GCD最大并發(fā)數(shù)

- (void)cjl_testSemaphore{
    /*
     應(yīng)用場(chǎng)景:同步當(dāng)鎖, 控制GCD最大并發(fā)數(shù)

     - dispatch_semaphore_create():創(chuàng)建信號(hào)量
     - dispatch_semaphore_wait():等待信號(hào)量誓竿,信號(hào)量減1磅网。當(dāng)信號(hào)量< 0時(shí)會(huì)阻塞當(dāng)前線程,根據(jù)傳入的等待時(shí)間決定接下來的操作——如果永久等待將等到信號(hào)(signal)才執(zhí)行下去
     - dispatch_semaphore_signal():釋放信號(hào)量筷屡,信號(hào)量加1涧偷。當(dāng)信號(hào)量>= 0 會(huì)執(zhí)行wait之后的代碼

     */
    dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_CONCURRENT);
    
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"當(dāng)前 - %d, 線程 - %@", i, [NSThread currentThread]);
        });
    }
    
    //利用信號(hào)量來改寫
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"當(dāng)前 - %d毙死, 線程 - %@", i, [NSThread currentThread]);
            
            dispatch_semaphore_signal(sem);
        });
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    }
}

dispatch_source_t

dispatch_source_t主要用于計(jì)時(shí)操作燎潮,其原因是因?yàn)樗鼊?chuàng)建的timer不依賴于RunLoop,且計(jì)時(shí)精準(zhǔn)度比NSTimer

- (void)cjl_testSource{
    /*
     dispatch_source
     
     應(yīng)用場(chǎng)景:GCDTimer
     在iOS開發(fā)中一般使用NSTimer來處理定時(shí)邏輯扼倘,但NSTimer是依賴Runloop的确封,而Runloop可以運(yùn)行在不同的模式下。如果NSTimer添加在一種模式下再菊,當(dāng)Runloop運(yùn)行在其他模式下的時(shí)候爪喘,定時(shí)器就掛機(jī)了;又如果Runloop在阻塞狀態(tài)纠拔,NSTimer觸發(fā)時(shí)間就會(huì)推遲到下一個(gè)Runloop周期秉剑。因此NSTimer在計(jì)時(shí)上會(huì)有誤差,并不是特別精確绿语,而GCD定時(shí)器不依賴Runloop秃症,計(jì)時(shí)精度要高很多
     
     dispatch_source是一種基本的數(shù)據(jù)類型候址,可以用來監(jiān)聽一些底層的系統(tǒng)事件
        - Timer Dispatch Source:定時(shí)器事件源吕粹,用來生成周期性的通知或回調(diào)
        - Signal Dispatch Source:監(jiān)聽信號(hào)事件源,當(dāng)有UNIX信號(hào)發(fā)生時(shí)會(huì)通知
        - Descriptor Dispatch Source:監(jiān)聽文件或socket事件源岗仑,當(dāng)文件或socket數(shù)據(jù)發(fā)生變化時(shí)會(huì)通知
        - Process Dispatch Source:監(jiān)聽進(jìn)程事件源匹耕,與進(jìn)程相關(guān)的事件通知
        - Mach port Dispatch Source:監(jiān)聽Mach端口事件源
        - Custom Dispatch Source:監(jiān)聽自定義事件源

     主要使用的API:
        - dispatch_source_create: 創(chuàng)建事件源
        - dispatch_source_set_event_handler: 設(shè)置數(shù)據(jù)源回調(diào)
        - dispatch_source_merge_data: 設(shè)置事件源數(shù)據(jù)
        - dispatch_source_get_data: 獲取事件源數(shù)據(jù)
        - dispatch_resume: 繼續(xù)
        - dispatch_suspend: 掛起
        - dispatch_cancle: 取消
     */
    
    //1.創(chuàng)建隊(duì)列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //2.創(chuàng)建timer
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    //3.設(shè)置timer首次執(zhí)行時(shí)間,間隔荠雕,精確度
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0*NSEC_PER_SEC, 0.1*NSEC_PER_SEC);
    //4.設(shè)置timer事件回調(diào)
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"GCDTimer");
    });
    //5.默認(rèn)是掛起狀態(tài)稳其,需要手動(dòng)激活
    dispatch_resume(timer);
    
}

NSOperation

NSOperation是基于GCD之上的更高一層封裝驶赏,NSOperation需要配合NSOperationQueue來實(shí)現(xiàn)多線程

NSOperatino實(shí)現(xiàn)多線程的步驟如下:

  • 1既鞠、創(chuàng)建任務(wù):先將需要執(zhí)行的操作封裝到NSOperation對(duì)象中煤傍。

  • 2、創(chuàng)建隊(duì)列:創(chuàng)建NSOperationQueue嘱蛋。

  • 3蚯姆、將任務(wù)加入到隊(duì)列中:將NSOperation對(duì)象添加到NSOperationQueue中。

//基本使用
- (void)cjl_testBaseNSOperation{
    //處理事務(wù)
    NSInvocationOperation *op =  [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation::) object:@"CJL"];
    //創(chuàng)建隊(duì)列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    //操作加入隊(duì)列
    [queue addOperation:op];
    
}
- (void)handleInvocation:(id)operation{
    NSLog(@"%@ - %@", operation, [NSThread currentThread]);
}

需要注意的是洒敏,NSOperation是個(gè)抽象類龄恋,實(shí)際運(yùn)用時(shí)中需要使用它的子類,有三種方式:

  • 1凶伙、使用子類NSInvocationOperation

//直接處理事務(wù)郭毕,不添加隱性隊(duì)列
- (void)cjl_createNSOperation{
    //創(chuàng)建NSInvocationOperation對(duì)象并關(guān)聯(lián)方法,之后start函荣。
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"CJL"];
    
    [invocationOperation start];
}
  • 2显押、使用子類NSBlockOperation
- (void)cjl_testNSBlockOperationExecution{
    //通過addExecutionBlock這個(gè)方法可以讓NSBlockOperation實(shí)現(xiàn)多線程。
    //NSBlockOperation創(chuàng)建時(shí)block中的任務(wù)是在主線程執(zhí)行傻挂,而運(yùn)用addExecutionBlock加入的任務(wù)是在子線程執(zhí)行的煮落。
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"main task = >currentThread: %@", [NSThread currentThread]);
    }];
    
    [blockOperation addExecutionBlock:^{
            NSLog(@"task1 = >currentThread: %@", [NSThread currentThread]);
    }];
    
    [blockOperation addExecutionBlock:^{
            NSLog(@"task2 = >currentThread: %@", [NSThread currentThread]);
    }];
    
    [blockOperation addExecutionBlock:^{
            NSLog(@"task3 = >currentThread: %@", [NSThread currentThread]);
    }];
    
    [blockOperation start];
}
  • 3、定義繼承自NSOperation的子類踊谋,通過實(shí)現(xiàn)內(nèi)部相應(yīng)的方法來封裝任務(wù)蝉仇。
//*********自定義繼承自NSOperation的子類*********
@interface CJLOperation : NSOperation
@end

@implementation CJLOperation
- (void)main{
    for (int i = 0; i < 3; i++) {
        NSLog(@"NSOperation的子類:%@",[NSThread currentThread]);
    }
}
@end

//*********使用*********
- (void)cjl_testCJLOperation{
    //運(yùn)用繼承自NSOperation的子類 首先我們定義一個(gè)繼承自NSOperation的類,然后重寫它的main方法殖蚕。
    CJLOperation *operation = [[CJLOperation alloc] init];
    [operation start];
}

NSOperationQueue

NSOperationQueue添加事務(wù)

NSOperationQueue有兩種隊(duì)列:主隊(duì)列轿衔、其他隊(duì)列。其他隊(duì)列包含了 串行和并發(fā)睦疫。

  • 主隊(duì)列:主隊(duì)列上的任務(wù)是在主線程執(zhí)行的害驹。

  • 其他隊(duì)列(非主隊(duì)列):加入到'非隊(duì)列'中的任務(wù)默認(rèn)就是并發(fā),開啟多線程蛤育。

- (void)cjl_testNSOperationQueue{
    /*
     NSInvocationOperation和NSBlockOperation兩者的區(qū)別在于:
     - 前者類似target形式
     - 后者類似block形式——函數(shù)式編程宛官,業(yè)務(wù)邏輯代碼可讀性更高
     
     NSOperationQueue是異步執(zhí)行的,所以任務(wù)一瓦糕、任務(wù)二的完成順序不確定
     */
    // 初始化添加事務(wù)
    NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任務(wù)1————%@",[NSThread currentThread]);
    }];
    // 添加事務(wù)
    [bo addExecutionBlock:^{
        NSLog(@"任務(wù)2————%@",[NSThread currentThread]);
    }];
    // 回調(diào)監(jiān)聽
    bo.completionBlock = ^{
        NSLog(@"完成了!!!");
    };
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:bo];
    NSLog(@"事務(wù)添加進(jìn)了NSOperationQueue");
}

設(shè)置執(zhí)行順序

//執(zhí)行順序
- (void)cjl_testQueueSequence{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        for (int i = 0; i < 5; i++) {
            [queue addOperationWithBlock:^{
                NSLog(@"%@---%d", [NSThread currentThread], i);
            }];
        }
}

設(shè)置優(yōu)先級(jí)

- (void)cjl_testOperationQuality{
    /*
     NSOperation設(shè)置優(yōu)先級(jí)只會(huì)讓CPU有更高的幾率調(diào)用底洗,不是說設(shè)置高就一定全部先完成
     - 不使用sleep——高優(yōu)先級(jí)的任務(wù)一先于低優(yōu)先級(jí)的任務(wù)二
     - 使用sleep進(jìn)行延時(shí)——高優(yōu)先級(jí)的任務(wù)一慢于低優(yōu)先級(jí)的任務(wù)二
     */
    NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 5; i++) {
            //sleep(1);
            NSLog(@"第一個(gè)操作 %d --- %@", i, [NSThread currentThread]);
        }
    }];
    // 設(shè)置最高優(yōu)先級(jí)
    bo1.qualityOfService = NSQualityOfServiceUserInteractive;
    
    NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"第二個(gè)操作 %d --- %@", i, [NSThread currentThread]);
        }
    }];
    // 設(shè)置最低優(yōu)先級(jí)
    bo2.qualityOfService = NSQualityOfServiceBackground;
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:bo1];
    [queue addOperation:bo2];

}

設(shè)置并發(fā)數(shù)

//設(shè)置并發(fā)數(shù)
- (void)cjl_testOperationMaxCount{
    /*
     在GCD中只能使用信號(hào)量來設(shè)置并發(fā)數(shù)
     而NSOperation輕易就能設(shè)置并發(fā)數(shù)
     通過設(shè)置maxConcurrentOperationCount來控制單次出隊(duì)列去執(zhí)行的任務(wù)數(shù)
     */
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.name = @"Felix";
    queue.maxConcurrentOperationCount = 2;
    
    for (int i = 0; i < 5; i++) {
        [queue addOperationWithBlock:^{ // 一個(gè)任務(wù)
            [NSThread sleepForTimeInterval:2];
            NSLog(@"%d-%@",i,[NSThread currentThread]);
        }];
    }
}

添加依賴

//添加依賴
- (void)cjl_testOperationDependency{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{
        [NSThread sleepForTimeInterval:0.5];
        NSLog(@"請(qǐng)求token");
    }];
    
    NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{
        [NSThread sleepForTimeInterval:0.5];
        NSLog(@"拿著token,請(qǐng)求數(shù)據(jù)1");
    }];
    
    NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{
        [NSThread sleepForTimeInterval:0.5];
        NSLog(@"拿著數(shù)據(jù)1,請(qǐng)求數(shù)據(jù)2");
    }];
    
    [bo2 addDependency:bo1];
    [bo3 addDependency:bo2];
    
    [queue addOperations:@[bo1,bo2,bo3] waitUntilFinished:YES];
    
    NSLog(@"執(zhí)行完了?我要干其他事");
}

線程間通訊

//線程間通訊
- (void)cjl_testOperationNoti{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.name = @"Felix";
    [queue addOperationWithBlock:^{
        NSLog(@"請(qǐng)求網(wǎng)絡(luò)%@--%@", [NSOperationQueue currentQueue], [NSThread currentThread]);
        
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"刷新UI%@--%@", [NSOperationQueue currentQueue], [NSThread currentThread]);
        }];
    }];

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市咕娄,隨后出現(xiàn)的幾起案子亥揖,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件费变,死亡現(xiàn)場(chǎng)離奇詭異摧扇,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)挚歧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門扛稽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人滑负,你說我怎么就攤上這事庇绽。” “怎么了橙困?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵瞧掺,是天一觀的道長。 經(jīng)常有香客問我凡傅,道長辟狈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任夏跷,我火速辦了婚禮哼转,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘槽华。我一直安慰自己壹蔓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布猫态。 她就那樣靜靜地躺著佣蓉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪亲雪。 梳的紋絲不亂的頭發(fā)上勇凭,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音义辕,去河邊找鬼虾标。 笑死,一個(gè)胖子當(dāng)著我的面吹牛灌砖,可吹牛的內(nèi)容都是我干的璧函。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼基显,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蘸吓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起续镇,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤美澳,失蹤者是張志新(化名)和其女友劉穎销部,沒想到半個(gè)月后摸航,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體制跟,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年酱虎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雨膨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡读串,死狀恐怖聊记,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情恢暖,我是刑警寧澤排监,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站杰捂,受9級(jí)特大地震影響舆床,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嫁佳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一挨队、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蒿往,春花似錦盛垦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蔬充,卻和暖如春俯在,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背娃惯。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國打工跷乐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人趾浅。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓愕提,卻偏偏與公主長得像,于是被迫代替她去往敵國和親皿哨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浅侨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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