概述
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
四種方案對比如下:
由于平時大多數(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í)行方式:
- 同步執(zhí)行
- 異步執(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_NOW
和DISPATCH_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_suspend
和dispatch_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
會把隊列的運行周期分為這三個過程:
- 首先等目前追加到并行隊列中所有任務(wù)都執(zhí)行完成
- 開始執(zhí)行
dispatch_barrier_async
中的任務(wù)這時候即便向并行隊列提交任務(wù)柔纵,也不會執(zhí)行 -
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
NSOperation
和NSOperationQueue
主要介紹以下幾個方面:
-
NSOperation
和NSOperationQueue
的用法介紹 -
NSOperation
的暫停、恢復(fù)和取消 - 通過KVO對
NSOperation
的狀態(tài)進行檢測 - 多個
NSOperation
之間的依賴關(guān)系 - 進程間通信
從簡單 意義上來說 伐割,NSOperation
是對GCD中的block進行的封裝候味,它也表示一個要被執(zhí)行的任務(wù)。和GCD的block類似口猜,NSOperation對象有一個start()
方法表示開始執(zhí)行這個任務(wù)负溪。
不僅如此,NSOperation
表示的任務(wù)還可以被取消济炎。它還有三種狀態(tài)isExecuted
川抡、isFinished
和isCancelled
以方便我們銅鼓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ù)并且把cancel
和finished
設(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é)合
NSOperation
和GCD
一起使用
最后胡诗,有個很經(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)并注明出處婿崭。