iOS開發(fā)之多線程(GCD與NSOperation)

概述

iOS開發(fā)中,多線程是必然碰到的讲衫,自己這兩天有空稍微總結(jié)了一下缕棵。簡單的概念如線程/進程等就不說了。

何為多線程涉兽?

多線程其實針對單核的CPU來設(shè)計的招驴,CPPU同一時間只能執(zhí)行一條線程,耳朵線程就是讓CPU快速的在多個線程之間進行調(diào)度

多線程優(yōu)點:

  • 能夠適當(dāng)提高資源利用率
  • 能夠適當(dāng)提高資源利用率

缺點:

  • 開線程需要一定的內(nèi)存空間枷畏,默認一條線程占用棧區(qū)間512KB
  • 線程過多會導(dǎo)致COU在線程上調(diào)度的開銷比較大
  • 程序設(shè)計比較復(fù)雜别厘,比如線程間通信,多線程的數(shù)據(jù)共享

在iOS中其實有4套多線程方案拥诡,它們分別是

  • pthread
  • NSThread
  • GCD
  • NSOperation

四種方案對比如下:

image

由于平時大多數(shù)只用到GCD和NSOperation触趴,下面就主要討論這兩種多線程方案實現(xiàn)

GCD簡介

GCD以block為基本單位,一個block中的代碼可以為一個任務(wù)渴肉。下文中提到 任務(wù) 冗懦,可以理解為執(zhí)行某個block

GCD有兩大重要概念,分別是隊列執(zhí)行方式仇祭;使用block的過程披蕉,概括來說就是把block放進合適的隊列,并選擇合適的執(zhí)行方式去執(zhí)行block的過程。

GCD有三種隊列:

  • 串行隊列(先進入隊列的任務(wù)先出隊列没讲,每次只執(zhí)行一個任務(wù))
  • 并發(fā)隊列 (依然是先進先出眯娱,不過可以形成多個任務(wù)并發(fā))
  • 主隊列 (這是一個特殊的串行隊列,而且隊列中的任務(wù) 一定會在主線程中執(zhí)行)

兩種執(zhí)行方式:

  1. 同步執(zhí)行
  2. 異步執(zhí)行

關(guān)于同步異步食零、串行并行和線程的關(guān)系困乒,如下表格所示

| 同步 | 異步
---|------|-------
主隊列|在主線程中執(zhí)行|在主線程中執(zhí)行
串行隊列|在當(dāng)前線程中執(zhí)行|新建線程執(zhí)行
并發(fā)隊列|在當(dāng)前線程中執(zhí)行|新建線程執(zhí)行

可以看到,同步方法不一定在本線程贰谣,異步方法亦不一定新開線程(主隊列)

所以娜搂,我們在編程時考慮的是同步Or異步 以及 串行Or 并行,而不是僅僅考慮是否新開線程

GCD死鎖問題

在使用GCD的過程中吱抚,如果向當(dāng)前串行隊列中同步派發(fā)一個任務(wù)百宇,就會導(dǎo)致死鎖,例如:

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"1"); //任務(wù)1
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2"); //任務(wù)2
    });
    NSLog(@"3"); //任務(wù)3    
}

參考文章:

以上代碼就發(fā)生了死鎖秘豹,控制臺只能打印1携御。因為我們目前在主隊列中,又將要同步地添加一個block到主隊列(串行)中既绕。

理論分析

dispatch_sync表示同步的執(zhí)行任務(wù)啄刹,也就是說執(zhí)行dispatch_sync后,當(dāng)前隊列會阻塞凄贩。而dispatch_sync中的block如果要在當(dāng)前隊列中執(zhí)行誓军,就得等待當(dāng)前隊列執(zhí)行完成。

上面例子中疲扎,首先主隊列執(zhí)行任務(wù)1昵时,然后執(zhí)行dispatch_sync,隨后在隊列中新增一個任務(wù)2椒丧。因為主隊列是同步隊列壹甥,所以任務(wù)2要等dispatch_sync執(zhí)行完才能執(zhí)行,但是dispatch_sync是同步派發(fā) 壶熏,要等任務(wù)2執(zhí)行完才算是結(jié)束句柠。在主隊列中的兩個任務(wù)互相等待,導(dǎo)致了死鎖棒假。當(dāng)然俄占,由于死鎖,后面添加的任務(wù)3也不會執(zhí)行了淆衷。

解決方案

通常情況下我們不必要用dispatch_sync缸榄,因為dispatch_async能夠更好地利用CPU,提升程序運行速度祝拯。

只有當(dāng)我們需要去報隊列中的任務(wù)必須順序執(zhí)行時甚带,才考慮使用dispatch_sync她肯。在使用dispatch_sync的時候應(yīng)該分析當(dāng)前處于哪個隊列,以及任務(wù)會提交到哪個隊列鹰贵。

GCD任務(wù)組

在開發(fā)中有這個需求晴氨,在A,B,C,D這四個任務(wù)全部結(jié)束后進行一些處理,那么我們怎么知道四個任務(wù)都已經(jīng)執(zhí)行完了呢碉输?這時候我們就需要用到dispatch_group了籽前。

dispatch_queue_t dispatchQueue = dispatch_queue_create("hyq.queue.next", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t dispatchGroup = dispatch_group_create();
    dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
        NSLog(@"任務(wù)A");
    });
    dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
        NSLog(@"任務(wù)B");
    });
    dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
        NSLog(@"任務(wù)C");
    });
    dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
        NSLog(@"任務(wù)D");
    });
    dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
        NSLog(@"end");
    });

首先我們要通過dispatch_group_create方法生成一個組

然后我們把dispatch_async方法換成dispatch_group_async。這個方法多了一個參數(shù)敷钾,第一個參數(shù)填剛創(chuàng)建的分組

最后調(diào)用dispatch_group_notify方法枝哄。這個方法表示把第三個參數(shù)block傳入第二個參數(shù)隊列中去。而且可以保證第三個參數(shù)block執(zhí)行時阻荒,group中所有任務(wù)已經(jīng)全部完成挠锥。

dispatch_group_wait

dispatch_group_wait的完整定義如下:

dispatch_group_wait(dispatch_group_t  _Nonnull group, dispatch_time_t timeout)

第一個參數(shù)表示要等待的group,第二個則表示等待時間。返回值表示經(jīng)過指定的等待時間侨赡,屬于這個group的任務(wù)是否已經(jīng)全部執(zhí)行完蓖租。如果是則返回0,否則返回非0.

第二個參數(shù)dispatch_time_t類型的參數(shù)還有兩個特殊值:DISPATCH_TIME_NOWDISPATCH_TIME_FOREVER羊壹,前者表示立刻檢查這個group的任務(wù)是否已經(jīng)完成蓖宦,后者則表示一直到屬于這個group的任務(wù)全部完成。

dispatch_after

通過GCD還可以進行簡單的定時 的操作油猫,比如在1秒后執(zhí)行某個block球昨。代碼如下:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"666");
    });

dispatch_after有三個參數(shù)。第一個表示時間眨攘,也就是從現(xiàn)在起往后1秒鐘。第二個參數(shù)表示提交到哪個隊列嚣州,第三個參數(shù)表示要提交的任務(wù)鲫售。

需要注意的是dispatch_after僅表示在指定時間后提交任務(wù) ,而非執(zhí)行任務(wù)该肴。如果任務(wù)提交到主隊列情竹,它將在main runloop中執(zhí)行,對于每隔1/60秒執(zhí)行一個的RunLoop1匀哄,任務(wù)最多可能在1+1/60秒后執(zhí)行秦效。

GCD進階

GCD也有一些強大的特性。接下來我們主要討論以下幾個部分:

  • dispatch_suspenddispatch_resume
  • dispatch_once
  • dispatch_barrier_async
  • dispatch_semaphore

我們知道NSOperationQueue有暫停suspend和恢復(fù)resume涎嚼。其實GCD中的隊列也有類似的功能阱州。

dispatch_suspend(dispatchQueue);
dispatch_resume(dispatchQueue);

這些函數(shù)不會影響到隊列中已經(jīng)執(zhí)行的任務(wù),隊列暫停后法梯,已經(jīng)添加到隊列中但是還沒有執(zhí)行的任務(wù) 不會執(zhí)行苔货,知道隊列被恢復(fù)犀概。

dispatch_once

dispatch_once在單例模式被廣泛使用

  • dispatch_once函數(shù)可以確保某個block在應(yīng)用程序執(zhí)行過程中只被處理一次,而且它是線程安全的夜惭。所以單例模式可以很簡單的實現(xiàn)姻灶,代碼實現(xiàn)如下:
+ (Manager *)sharedInstance {
    static Manager *sharedManagerInstance = nil;
    static dispatch_once_t once;

    dispatch_once($once, ^{
        sharedManagerInstance = [[Manager alloc] init];
    });

    return sharedManagerInstance;
}

這段代碼中我們創(chuàng)建一個值為nil的sharedManagerInstance靜態(tài)對象,然后把它的初始化代碼放到dispatch_once中完成诈茧。

這樣产喉,只有第一次調(diào)用sharedInstance方法時才會進行對象的初始化,以后每次只是返回sharedManagerInstance而已敢会。

dispatch_barrier_async

我們知道在寫入時曾沈,不能再其他線程讀取或?qū)懭搿5嵌鄠€線程同時讀取數(shù)據(jù)是沒有問題的走触。所以我們可以把讀取任務(wù)放入并行隊列晦譬,把寫入任務(wù)放入串行隊列,并且保證寫入任務(wù)執(zhí)行過程中沒有讀取任務(wù)可以執(zhí)行互广。

這樣的需求就比較常見敛腌,GCD提供了一個非常簡單的解決方法dispatch_barrier_async

假設(shè)我們有 四個讀取任務(wù),在第二惫皱、三個任務(wù)之間有一個寫入任務(wù)像樊,代碼大概如下:

dispatch_queue_t dispatchQueue = dispatch_queue_create("hyq.queue.next", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, block1_for_reading)  
dispatch_async(queue, block2_for_reading)

/*
    這里插入寫入任務(wù),比如:
    dispatch_async(queue, block_for_writing)
*/

dispatch_async(queue, block3_for_reading)  
dispatch_async(queue, block4_for_reading)

如果代碼這樣寫旅敷,由于幾個block是并發(fā)執(zhí)行生棍,就有可能在前兩個block中讀取到已經(jīng)修改了的數(shù)據(jù)。如果是有多寫入任務(wù)媳谁,那問題更嚴重涂滴,可能會有數(shù)據(jù)競爭。

如果使用dispatch_barrier_async函數(shù)晴音,代碼就可以這么寫:

dispatch_async(queue, block1_for_reading)  
dispatch_async(queue, block2_for_reading)

dispatch_barrier_async(queue, block_for_writing)

dispatch_async(queue, block3_for_reading)  
dispatch_async(queue, block4_for_reading) 

dispatch_barrier_async會把隊列的運行周期分為這三個過程:

  1. 首先等目前追加到并行隊列中所有任務(wù)都執(zhí)行完成
  2. 開始執(zhí)行dispatch_barrier_async中的任務(wù)這時候即便向并行隊列提交任務(wù)柔纵,也不會執(zhí)行
  3. dispatch_barrier_async中任務(wù)執(zhí)行完成后,并行隊列恢復(fù)正常锤躁。

總的來說搁料,dispatch_barrier_async起到了承上啟下的作用。它保證此前的任務(wù)都先于自己執(zhí)行系羞,此后的任務(wù)也遲于自己執(zhí)行郭计。正如barrier的含義一樣,它起到一個柵欄或者分水嶺的作用椒振。

使用并行隊列和diapatch_barrier_async方法昭伸,就可以高效的進行數(shù)據(jù)和文件讀寫了。

dipactch_semaphore

首先介紹一下信號量(semaphore)的概念澎迎。信號量是持有計數(shù)的信號勋乾,舉個生活中的例子來看:

假設(shè)有一個房子宋下,它對應(yīng)進程的概念,房子里的人就對應(yīng)著線程辑莫。一個進程可以包括多個線程学歧。這個房子(進程)有很多資源,比如花園各吨、客廳燈枝笨,是所有人(線程)共享的。

但是有些地方揭蜒,比如臥室横浑,最多只有兩個人進去睡覺。怎么辦呢》在臥室門口掛上兩把鑰匙屉更。進去的人(線程)就拿著要是進去徙融,沒有鑰匙就不能進去,出來的時候把鑰匙放回門口瑰谜。

這時候欺冀,門口的鑰匙數(shù)量就稱為信號量(Semaphore)。很明顯萨脑,信號量為0時需要等地隐轩,信號量不為零時,減去1而且不等待渤早。

在GCD中职车,創(chuàng)建信號量的代碼如下:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);

這句代碼通過diapatch_semaphore_create方法創(chuàng)建一個信號量初始值為3.然后就可以調(diào)用dispatch_semaphore_wait方法了。

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

dispatch_semaphore_wait方法表示一直等待直到信號量的值大于等于1鹊杖,當(dāng)這個方法執(zhí)行后悴灵,會把第一個信號量參數(shù)的值減1。

第二個參數(shù)是一個dispatch_time_t類型的時間骂蓖,它表示這個方法最大的等待時間积瞒。

返回值也和dispatch_group_wait方法一樣,返回0表示在規(guī)定的時間內(nèi) 第一個參數(shù)信號量的值已經(jīng)大于等于1涯竟,否則表示已超過規(guī)定等待時間,但信號量的值還是0.

dispatch_semaphore_wait方法返回0空厌,因為此時的信號量的值大于等于1庐船,任務(wù)獲得了可以執(zhí)行的權(quán)限。這時候我們就可以安全的執(zhí)行需要進行排他控制的任務(wù)了嘲更。

任務(wù)結(jié)束時還需要調(diào)用dispatch_semaphore_signal()方法筐钟,將信號量的值加1.這類似于之前所說的,從臥室出來要把鎖放回門上赋朦,否則后來的人就無法進入了篓冲。示例代碼如下:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

    dispatch_queue_t diapatchQueue = dispatch_queue_create("hyq.gcd.next", DISPATCH_QUEUE_CONCURRENT);

    NSMutableArray *array = [NSMutableArray array];

    for (int i = 0; i < 100000; i++) {
        dispatch_async(diapatchQueue, ^{

            /*
                某個線程執(zhí)行到這里李破,如果信號量為1,那么wait方法返回1壹将,開始執(zhí)行接下來的操作嗤攻。與此同時,因為信號量
                變?yōu)?诽俯,其他執(zhí)行到這里的線程必須等待
             */
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

            /*
                執(zhí)行了wait方法后妇菱,信號量變成了0.可以進行接下來的操作。這時候其他線程都得等待wait方法返回
                可以對array修改的線程在任意時刻都只有一個暴区,可以安全的修改array
             */
            [array addObject:@(i)];

            /*
                排他操作執(zhí)行結(jié)束闯团,記得要調(diào)用signal方法,把信號量的值加1.這樣仙粱,如果有別的線程在等待wait函數(shù)返回房交,就由最先等待的線程執(zhí)行
             */
            dispatch_semaphore_signal(semaphore);
        });
    }

NSOperation

NSOperationNSOperationQueue主要介紹以下幾個方面:

  1. NSOperationNSOperationQueue的用法介紹
  2. NSOperation的暫停、恢復(fù)和取消
  3. 通過KVO對NSOperation的狀態(tài)進行檢測
  4. 多個NSOperation之間的依賴關(guān)系
  5. 進程間通信

從簡單 意義上來說 伐割,NSOperation是對GCD中的block進行的封裝候味,它也表示一個要被執(zhí)行的任務(wù)。和GCD的block類似口猜,NSOperation對象有一個start()方法表示開始執(zhí)行這個任務(wù)负溪。

不僅如此,NSOperation表示的任務(wù)還可以被取消济炎。它還有三種狀態(tài)isExecuted川抡、isFinishedisCancelled以方便我們銅鼓KVC對它的狀態(tài)進行監(jiān)聽。

想要開始執(zhí)行一個任務(wù)可以這么寫:

NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"task----%@",[NSThread currentThread]);
    }];
    [op start];

打印結(jié)果如下:

[21921:884043] task----<NSThread: 0x608000063980>{number = 1, name = main}

我們創(chuàng)建了一個NSBlockOperation,并且設(shè)置好它的block须尚,也就是要執(zhí)行的任務(wù)崖堤。這個任務(wù)就會在主線程中執(zhí)行。為什么不直接使用NSOperation呢耐床?

  • 因為NSOperation本身是一個抽象類密幔,要使用可以通過以下幾個方法:

    • 使用NSInvocationOperation
    • 使用NSBlockOperation
    • 自定義NSOperation的子類

NSBlockOperation可以用來封裝一個或多個block,后面會介紹如何自定義NSOperation的子類撩轰。

同時胯甩,還可以調(diào)用addExecutionBlock方法追加幾個任務(wù),這些任務(wù)會并行執(zhí)行(也就是說很有可能運行在別的線程里)

最后堪嫂,調(diào)用start方法讓NSOperation方法運行起來偎箫。start是一個同步方法。

NSOpaerationQueue

從上面我們知道皆串,默認的NSOperation是同步執(zhí)行的淹办。簡單的看一下NSOperation類的定義會發(fā)現(xiàn)它只有一個只讀屬性asynchronous

這意味著如果想要異步執(zhí)行,就需要自定義NSOperation的子類恶复×或者使用NSOperationQueue

NSOperationQueue類似于GCD中的隊列速挑。我們知道GCD中的隊列有三種:主隊列、串行隊列和并行隊列副硅。NSOperationQueue更簡單姥宝,只有兩種:主隊列非主隊列

我們自己生成的NSOperationQueue對象都是非主隊列想许,主隊列可以用[NSOperationQueue mainQueue]取得伶授。

NSOperationQueue的主隊列是串行隊列,而且其中所有NSOperation都會在主線程中執(zhí)行流纹。

對于非主隊列來說糜烹,一旦一個NSOperation被放入其中,那這個NSOperation一定是兵法執(zhí)行的漱凝。因為NSOperationQueue會為每一個NSOperation創(chuàng)建線程并調(diào)用它的start方法疮蹦。

NSOperationQueue有一個屬性叫maxConcurrentOperationCount,它表示最多支持多少個NSOperation并發(fā)執(zhí)行茸炒。如果maxConCurrentOperationCount被設(shè)置為1愕乎,就以為這個隊列是串行隊列。

因此壁公,NSOperationQueue和GCD中的隊列有這樣的對應(yīng)關(guān)系:

| NSOperation | GCD
---|------------|----
主隊列 | [NSOperationQueue mainQueue] | dispatch_get_mian_queue()
串行隊列 | 自建隊列 maxConcurrentOperationCount為1 | dispatch_queue_create("",DISPATCH_QUEUE_SERIAL)
并發(fā)隊列 | 自建隊列 maxConcurrentOperationCount大于1 | dispatch_queue_create("",DISPATCH_QUEUE_CONCURRENT)

那么如何利用NSOperationQueue實現(xiàn)異步操作感论?代碼如下:

    //自建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"task0---%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"task1---%@", [NSThread currentThread]);

    }];
    [op addExecutionBlock:^{
        NSLog(@"task2---%@", [NSThread currentThread]);

    }];
    [op addExecutionBlock:^{
        NSLog(@"task3---%@", [NSThread currentThread]);

    }];
    [queue addOperation:op];
    NSLog(@"操作結(jié)束");

執(zhí)行結(jié)果如下:

2017-02-20 14:47:18.074 GCDAndNSOperation[22578:915368] task2---<NSThread: 0x60000026a9c0>{number = 5, name = (null)}
2017-02-20 14:47:18.074 GCDAndNSOperation[22578:915371] task0---<NSThread: 0x60000026a980>{number = 3, name = (null)}
2017-02-20 14:47:18.074 GCDAndNSOperation[22578:915401] task3---<NSThread: 0x60800026a6c0>{number = 6, name = (null)}
2017-02-20 14:47:18.074 GCDAndNSOperation[22578:915369] task1---<NSThread: 0x60800026a580>{number = 4, name = (null)}
2017-02-20 14:47:18.074 GCDAndNSOperation[22578:915176] 操作結(jié)束

使用NSOperationQueue來執(zhí)行任務(wù)與之前的區(qū)別在于,首先創(chuàng)建一個非主隊列紊册。然后用addOperation方法替換之前的start方法比肄。剛剛已經(jīng)說過,NSOperationQueue會為每一個NSOperation創(chuàng)建線程并調(diào)用它們的start方法囊陡。

觀察一下運行結(jié)果 芳绩,所有的NSOperation都沒有在主線程執(zhí)行,從而成功的實現(xiàn)了異步撞反、并行處理妥色。

取消任務(wù)

如果我們有兩次網(wǎng)絡(luò)請求,第二次請求會用到第一次的數(shù)據(jù)遏片。假設(shè)此時網(wǎng)絡(luò)情況不好嘹害,第一次請求超時了,那么第二次請求也沒有必要發(fā)送了吮便。當(dāng)然笔呀,用戶也有可能人為地取消某個NSOperation

當(dāng)某個NSOperation被取消時线衫,我們應(yīng)該盡可能的清除NSOperation內(nèi)部的數(shù)據(jù)并且把cancelfinished設(shè)為true凿可,把executing設(shè)為false惑折。

     //取消某個NSOperation
    [operation cancel];
    //取消某個NSOperationQueue剩余的NSOperation
    [queue cancelAllOperations];

設(shè)置依賴

有時候一個網(wǎng)絡(luò)請求是用到另一個網(wǎng)絡(luò)請求獲得的數(shù)據(jù)授账,這時候我們要確保第二次請求的手第一個請求已經(jīng)執(zhí)行完枯跑。但是我們同時還希望用到NSOperationQueue的并發(fā)特性(因為可能不止這兩個任務(wù))

這時候我們可以設(shè)置NSOperation之間的依賴關(guān)系,很簡單白热,代碼如下:

[operation1 addDependency: operation2];

需要注意的是NSOperation之間的相互依賴會導(dǎo)致死鎖

NSOperationQueue暫停與恢復(fù)

這個也很簡單敛助,只要修改suspended屬性即可

queue.suspended = true; //暫停queue中所有operation
queue.suspended = false; //恢復(fù)queue中所有operation

NSOperation優(yōu)先級

GCD中,任務(wù)(block)是沒有優(yōu)先級的屋确,而隊列具有優(yōu)先級纳击。和GCD相反,我們一般考慮NSOperation的優(yōu)先級

NSOperation有一個NSOperationQueuePriority枚舉類型的屬性queuePriority

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

需要注意的是攻臀,NSOperationQueue也不能完全保證優(yōu)先級高的任務(wù) 一定先執(zhí)行

進程間通信

有時候我們在子線程中執(zhí)行完一些操作的時候焕数,需要回到主線程做一些事情(如進行UI操作),因此需要從當(dāng)前線程回到主線程刨啸,以下載并顯示圖片為例堡赔,代碼如下:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 子線程下載圖片
[queue addOperationWithBlock:^{
    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 alloc] initWithData:data];
    // 回到主線程進行顯示
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        self.imageView.image = image;
    }];
}];

小結(jié)

NSOperation和GCD如何選擇

  • GCD以block為單位,代碼簡潔设联。同時GCD中的隊列善已、組、信號量离例、source换团、barriers都是組成并行編程的基本原語。對于一次性的計算宫蛆,或者僅僅為了加快現(xiàn)有方法的運行速度艘包,選擇輕量化的GCD就更加方便
  • NSOperation可以用來規(guī)劃一組任務(wù)之間的依賴關(guān)系,設(shè)置它們的優(yōu)先級洒扎,任務(wù)能被取消辑甜。隊列可以暫停、恢復(fù)袍冷。NSOperation還可以自定義子類磷醋。這些都是GCD沒有具備的。
  • 可以根據(jù)情況有效結(jié)合NSOperationGCD一起使用

最后胡诗,有個很經(jīng)典的面試題邓线,GCD和NSOperation有什么區(qū)別

答案基本就是對上面所說的的總結(jié)

  • GCD是純C語言的API,NSOperation是基于GCD的OC版本封裝
  • GCD只支持FIFO的隊列煌恢,NSOperation可以很方便地調(diào)整執(zhí)行順序骇陈,設(shè)置最大并發(fā)數(shù)量
  • NSOperationQueue可以輕松在operation間設(shè)置依賴關(guān)系,而GCD需要些很多代碼才能實現(xiàn)
  • NSOperationQueue支持KVO瑰抵,可以檢測operation是否正在執(zhí)行(isExecuted)你雌,是否結(jié)束(isFinisn),是否取消(isCancel)
  • GCD的執(zhí)行速度比NSOperation快

參考文章 :
iOS多線程編程總結(jié)
關(guān)于iOS多線程,你看我就夠了

作者:小球why
鏈接:http://www.reibang.com/p/5593af00c597
來源:簡書
簡書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請聯(lián)系作者獲得授權(quán)并注明出處婿崭。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拨拓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子氓栈,更是在濱河造成了極大的恐慌渣磷,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件授瘦,死亡現(xiàn)場離奇詭異醋界,居然都是意外死亡,警方通過查閱死者的電腦和手機提完,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門形纺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人徒欣,你說我怎么就攤上這事挡篓。” “怎么了帚称?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵官研,是天一觀的道長。 經(jīng)常有香客問我闯睹,道長戏羽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任楼吃,我火速辦了婚禮始花,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘酷宵。我一直安慰自己躬窜,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布荣挨。 她就那樣靜靜地躺著男韧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪此虑。 梳的紋絲不亂的頭發(fā)上口锭,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天朦前,我揣著相機與錄音,去河邊找鬼。 笑死韭寸,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播臭胜,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼耸三,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了仪壮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤爽彤,失蹤者是張志新(化名)和其女友劉穎适篙,沒想到半個月后箫爷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡硫痰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年窜护,在試婚紗的時候發(fā)現(xiàn)自己被綠了柱徙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡藏研,死狀恐怖概行,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤禽炬,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布腹尖,位于F島的核電站伐脖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏讼庇。R本人自食惡果不足惜蠕啄,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望和媳。 院中可真熱鬧哈街,春花似錦、人聲如沸撼港。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽靶溜。三九已至懒震,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瓷炮,已是汗流浹背递宅。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留烘绽,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓翔忽,卻偏偏與公主長得像盏檐,于是被迫代替她去往敵國和親糯笙。 傳聞我的和親對象是個殘疾皇子撩银,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

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