GCD介紹
Grand Central Dispatch (GCD) 是 Apple 開發(fā)的一個多核編程的解決方法帝洪。
一似舵、主要概括
- 和operation queue一樣都是基于隊列的并發(fā)編程API,他們通過集中管理大家協(xié)同使用的線程池葱峡。
- 公開的5個不同隊列:運(yùn)行在主線程中的main queue砚哗,3個不同優(yōu)先級的后臺隊列(High Priority Queue,Default Priority Queue族沃,Low Priority Queue)频祝,以及一個優(yōu)先級更低的后臺隊列Background Priority Queue(用于I/O)
- 可創(chuàng)建自定義隊列:串行或并列隊列。自定義一般放在Default Priority Queue和Main Queue里脆淹。
- 操作是在多線程上還是單線程主要是看隊列的類型和執(zhí)行方法常空,并發(fā)隊列異步執(zhí)行才能在多線程,并發(fā)隊列同步執(zhí)行就只會在這個并發(fā)隊列在隊列中被分配的那個線程執(zhí)行盖溺。
二漓糙、基本概念
1.串行與并發(fā)
Serial Queues 串行隊列
串行隊列中的任務(wù)一次執(zhí)行一個,每個任務(wù)只在前一個任務(wù)完成時才開始烘嘱。
這些任務(wù)的執(zhí)行時機(jī)受到 GCD 的控制昆禽;唯一能確保的事情是 GCD 一次只執(zhí)行一個任務(wù)蝗蛙,并且按照我們添加到隊列的順序來執(zhí)行。
dispatch_queue_t queue1 = dispatch_queue_create("serialqueue", DISPATCH_QUEUE_SERIAL);
for (int i=0; i<100; i++) {
dispatch_async(queue1, ^{
NSLog(@"%d",i);
});
}
打印順序一定是1.2.3......98.99醉鳖。保證了執(zhí)行順序捡硅。
2.Concurrent Queues 并發(fā)隊列
在并發(fā)隊列中的任務(wù)能得到的保證是它們會按照被添加的順序開始執(zhí)行,但這就是全部的保證了盗棵。任務(wù)可能以任意順序完成壮韭,你不會知道何時開始運(yùn)行下一個任務(wù),或者任意時刻有多少 Block 在運(yùn)行纹因。
何時開始一個 Block 完全取決于 GCD 喷屋。如果一個 Block 的執(zhí)行時間與另一個重疊屈张,也是由 GCD 來決定是否將其運(yùn)行在另一個不同的核心上菊霜,如果那個核心可用,否則就用上下文切換的方式來執(zhí)行不同的 Block 宁赤。
dispatch_queue_t queue = dispatch_queue_create("concurrentqueue", DISPATCH_QUEUE_CONCURRENT);
for (int i=0; i<100; i++) {
dispatch_async(queue, ^{
NSLog(@"%d",i);
});
}
打印順序1.2.3.4....55.56.53.....20..99惊畏,按照順序添加進(jìn)隊列恶耽,但執(zhí)行結(jié)果并不能保證順序,結(jié)果與GCD的調(diào)度有關(guān)陕截。
2.GCD中的隊列
我們可以通過dispatch_queue_create
的方式手動創(chuàng)建隊列驳棱,通過第二個參數(shù)設(shè)置隊列的類型。既上面提到的(串行或并發(fā))隊列农曲。
dispatch_queue_t queue = dispatch_queue_create("queue-label", DISPATCH_QUEUE_CONCURRENT);
除此之外,系統(tǒng)還為我們提供了5種全局隊列
- Main Dispatch Queue 類型:Serial Dispatch Queue 主線程執(zhí)行
- Global Dispatch Queue (HIGH) 類型:Concurrent Dispatch Queue 執(zhí)行優(yōu)先級:高
- Global Dispatch Queue (DEFAULT) 類型:Concurrent Dispatch Queue 執(zhí)行優(yōu)先級:默認(rèn)
- Global Dispatch Queue (LOW) 類型:Concurrent Dispatch Queue 執(zhí)行優(yōu)先級:低
- Global Dispatch Queue (BACKGROUND) 類型: Concurrent Dispatch Queue 執(zhí)行優(yōu)先級:后臺
dispatch_queue_t
dispatch_get_main_queue(void)
{
return DISPATCH_GLOBAL_OBJECT(dispatch_queue_t, _dispatch_main_q);
}
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
我們可以發(fā)現(xiàn)驻债,除了Main Dispatch Queue是串行隊列乳规,其他的均為并發(fā)隊列,并且擁有不同的優(yōu)先級合呐。
我們可以通過以下方式獲取這些全局隊列
//獲取主隊列
dispatch_queue_t main_queue = dispatch_get_main_queue();
//通過第一個參數(shù)獲取其他全局隊列暮的。
dispatch_queue_t global_queue = dispatch_get_global_queue(`DISPATCH_QUEUE_PRIORITY_HIGH`, 0);
自定義隊列的優(yōu)先級
我們可以看到,系統(tǒng)提供的全局隊列的不同之處就是隊列的優(yōu)先級不同淌实。不同的優(yōu)先級適合于不同的業(yè)務(wù)場景冻辩。GCD中提供了以下幾種隊列優(yōu)先級:
__QOS_ENUM(qos_class, unsigned int,
QOS_CLASS_USER_INTERACTIVE
__QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x21,
QOS_CLASS_USER_INITIATED
__QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x19,
QOS_CLASS_DEFAULT
__QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x15,
QOS_CLASS_UTILITY
__QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x11,
QOS_CLASS_BACKGROUND
__QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x09,
QOS_CLASS_UNSPECIFIED
__QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x00,
);
- QOS_CLASS_USER_INTERACTIVE:user interactive等級表示任務(wù)需要被立即執(zhí)行提供好的體驗,用來更新UI拆祈,響應(yīng)事件等恨闪。這個等級最好保持小規(guī)模。
- QOS_CLASS_USER_INITIATED:user initiated等級表示任務(wù)由UI發(fā)起異步執(zhí)行放坏。適用場景是需要及時結(jié)果同時又可以繼續(xù)交互的時候咙咽。
- QOS_CLASS_UTILITY:utility等級表示需要長時間運(yùn)行的任務(wù),伴有用戶可見進(jìn)度指示器淤年。經(jīng)常會用來做計算钧敞,I/O蜡豹,網(wǎng)絡(luò),持續(xù)的數(shù)據(jù)填充等任務(wù)溉苛。這個任務(wù)節(jié)能镜廉。
- QOS_CLASS_BACKGROUND:background等級表示用戶不會察覺的任務(wù),使用它來處理預(yù)加載愚战,或者不需要用戶交互和對時間不敏感的任務(wù)娇唯。
我們可以通過以下方式為自定義的隊列設(shè)置隊列優(yōu)先級
1.使用dispatch_queue_attr_make_with_qos_class自定義一個隊列描述。
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, -1);
dispatch_queue_t queue = dispatch_queue_create("com.starming.gcddemo.qosqueue", attr);
dispatch_queue_attr_make_with_qos_class的參數(shù)凤巨。
1.dispatch_queue_attr_t:串行或者并發(fā)
2.dispatch_qos_class_t:填寫上面的調(diào)度類型视乐。
3.relative_priority:qos內(nèi)的相對優(yōu)先級,這個參數(shù)必須比0大比設(shè)置的QOS Class小敢茁。
2.使用dispatch_set_target_queue設(shè)置與目標(biāo)相同類型的隊列佑淀。
dispatch_queue_t queue = dispatch_queue_create("com.starming.gcddemo.settargetqueue",NULL); //需要設(shè)置優(yōu)先級的queue
dispatch_queue_t referQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //參考優(yōu)先級
dispatch_set_target_queue(queue, referQueue); //設(shè)置queue和referQueue的優(yōu)先級一樣
dispatch_set_target_queue:可以設(shè)置優(yōu)先級,也可以設(shè)置隊列層級體系彰檬,比如讓多個串行和并發(fā)隊列在統(tǒng)一一個串行隊列里串行執(zhí)行.
dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t firstQueue = dispatch_queue_create("com.starming.gcddemo.firstqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t secondQueue = dispatch_queue_create("com.starming.gcddemo.secondqueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(firstQueue, serialQueue);
dispatch_set_target_queue(secondQueue, serialQueue);
dispatch_async(firstQueue, ^{
NSLog(@"1");
});
dispatch_async(secondQueue, ^{
NSLog(@"2");
});
dispatch_async(secondQueue, ^{
NSLog(@"3");
});
3.異步和同步
同步和異步的概念主要針對的是我們的線程伸刃。
- 同步是指在當(dāng)前線程下執(zhí)行隊列中的任務(wù),并且同步任務(wù)執(zhí)行完畢后才執(zhí)行后面的任務(wù)逢倍。
- 異步是指在子線程(非當(dāng)前線程)下執(zhí)行隊列中的任務(wù)捧颅。執(zhí)行任務(wù)的順序與當(dāng)前線程中的任務(wù)無關(guān)。
在GCD中较雕,異步執(zhí)行使用的是dispatch_async
碉哑,同步執(zhí)行使用dispatch_sync
。
dispatch_sync同步執(zhí)行
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"First Log");
});
NSLog(@"Second Log");
}
下面是圖中幾個步驟的說明:
1.主隊列一路按順序執(zhí)行任務(wù)——接著是一個實(shí)例化 UIViewController 的任務(wù)亮蒋,其中包含了 viewDidLoad 扣典。
2.viewDidLoad 在主線程執(zhí)行。
3.主線程目前在 viewDidLoad 內(nèi)慎玖,正要到達(dá) dispatch_sync 贮尖。
4.dispatch_sync Block 被添加到一個全局隊列中,將在稍后執(zhí)行趁怔。進(jìn)程將在主線程掛起直到該 Block 完成湿硝。同時,全局隊列并發(fā)處理任務(wù)润努;要記得 Block 在全局隊列中將按照 FIFO 順序出列关斜,但可以并發(fā)執(zhí)行。
5.全局隊列處理 dispatch_sync Block 加入之前已經(jīng)出現(xiàn)在隊列中的任務(wù)任连。
6.終于蚤吹,輪到 dispatch_sync Block 。
7.這個 Block 完成,因此主線程上的任務(wù)可以恢復(fù)裁着。
8.viewDidLoad 方法完成繁涂,主隊列繼續(xù)處理其他任務(wù)。
小結(jié):dispatch_sync 添加任務(wù)到一個隊列并等待直到任務(wù)完成二驰。dispatch_async 做類似的事情扔罪,但不同之處是它不會等待任務(wù)的完成,而是立即繼續(xù)“調(diào)用線程”的其它任務(wù)桶雀。
使用dispatch_async
矿酵。
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"First Log");
});
NSLog(@"Second Log");
}
1.主隊列一路按順序執(zhí)行任務(wù)——接著是一個實(shí)例化 UIViewController 的任務(wù),其中包含了 viewDidLoad 矗积。
2.viewDidLoad 在主線程執(zhí)行全肮。
3.主線程目前在 viewDidLoad 內(nèi),正要到達(dá) dispatch_async 棘捣。
4.dispatch_async Block 被添加到一個全局隊列中辜腺,將在稍后執(zhí)行。
5.viewDidLoad 在添加 dispatch_async 到全局隊列后繼續(xù)進(jìn)行乍恐,主線程把注意力轉(zhuǎn)向剩下的任務(wù)评疗。同時,全局隊列并發(fā)地處理它未完成地任務(wù)茵烈。記住 Block 在全局隊列中將按照 FIFO 順序出列百匆,但可以并發(fā)執(zhí)行。
6.添加到 dispatch_async 的代碼塊開始執(zhí)行呜投。
7.dispatch_async Block 完成加匈,兩個 NSLog 語句將它們的輸出放在控制臺上。
小結(jié):在這個特定的實(shí)例中仑荐,第二個 NSLog 語句執(zhí)行矩动,跟著是第一個 NSLog 語句。并不總是這樣——著取決于給定時刻硬件正在做的事情释漆,而且你無法控制或知曉哪個語句會先執(zhí)行±河“第一個” NSLog 在某些調(diào)用情況下會第一個執(zhí)行男图。
三、GCD的使用
1.用 dispatch_async 處理后臺任務(wù)
通過異步執(zhí)行下載任務(wù)可以避免界面被一些耗時操作卡死甜橱,例如讀取網(wǎng)絡(luò)數(shù)據(jù)逊笆,大數(shù)據(jù)IO,還有大量數(shù)據(jù)的數(shù)據(jù)庫讀寫岂傲,這時需要在另一個線程中處理难裆,然后通知主線程更新界面。我們常常會經(jīng)常這樣使用GCD
//代碼框架
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 耗時的操作
dispatch_async(dispatch_get_main_queue(), ^{
// 更新界面
});
});
當(dāng)我們需要下載一張圖片褂痰,并顯示到界面時缩歪,我們可以這樣處理。防止界面因下載圖片被卡死谍憔。
//下載圖片的示例
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL * url = [NSURL URLWithString:@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"];
NSData * data = [[NSData alloc]initWithContentsOfURL:url];
UIImage *image = [[UIImage alloc]initWithData:data];
if (data != nil) {
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
}
});
運(yùn)行邏輯:
- 創(chuàng)建一個并發(fā)隊列并使用dispatch_async異步執(zhí)行這個隊列里面的任務(wù)
- 在子線程中下載圖片數(shù)據(jù)匪蝙,生成對應(yīng)的UIimage對象。
- 異步切換到主線程隊列习贫,修改UI逛球。
2.dispatch_after延后執(zhí)行
dispatch_after只是延時提交block,不是延時立刻執(zhí)行
NSLog(@"1");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
打印順序是1苫昌,3颤绕,2。dispatch_after并不會阻塞當(dāng)前線程蜡歹,只是延遲提交block屋厘,在當(dāng)前線程繼續(xù)執(zhí)行。
其中的dispatch time參數(shù)的函數(shù)原型如下:
dispatch_time_t dispatch_time ( dispatch_time_t when, int64_t delta );
第一個參數(shù)為DISPATCH_TIME_NOW表示當(dāng)前月而。第二個參數(shù)的delta表示納秒汗洒,一秒對應(yīng)的納秒為1000000000,系統(tǒng)提供了一些宏來簡化
#define NSEC_PER_SEC 1000000000ull //每秒有多少納秒
#define USEC_PER_SEC 1000000ull //每秒有多少毫秒
#define NSEC_PER_USEC 1000ull //每毫秒有多少納秒
3.dispatch_once創(chuàng)建一個線程安全的單例
先說說不安全的創(chuàng)建情況
+ (instancetype)sharedManager {
static SingleClass *manager = nil;
if(!manager) {
manager = [SingleClass new];
}
return manager;
}
當(dāng)我們在不同線程多次調(diào)用改方法時父款,很有可能發(fā)生A線程進(jìn)入if代碼塊后溢谤,系統(tǒng)切換上下文世杀,B線程也進(jìn)入代碼塊造成對象被初始化2次的問題。于是我們就需要保證單例的線程安全所刀。
static SingleClass *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [SingleClass new];
});
return manager;
其實(shí)就是使用dispatch_once
取代if
條件判斷,dispatch_once()
以線程安全的方式執(zhí)行且僅執(zhí)行其代碼塊一次砌函。試圖訪問臨界區(qū)(即傳遞給 dispatch_once
的代碼)的不同的線程會在臨界區(qū)已有一個線程的情況下被阻塞垦沉,直到臨界區(qū)完成為止摧玫。
4.dispatch_apply進(jìn)行快速迭代
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(999, queue, ^(size_t i) {
NSLog(@"%d",i);
});
NSLog(@"end");
輸出結(jié)果1,2....998,end
說明這個方法是會阻塞當(dāng)前線程的闸婴。并且可以循環(huán)執(zhí)行并發(fā)操作降狠。它的優(yōu)點(diǎn)在于執(zhí)行并發(fā)操作時GCD會進(jìn)行線程管理。
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT);
if (explode) {
//有問題的情況,可能會死鎖
for (int i = 0; i < 999 ; i++) {
dispatch_async(concurrentQueue, ^{
NSLog(@"wrong %d",i);
//do something hard
});
}
} else {
//會優(yōu)化很多,能夠利用GCD管理
dispatch_apply(999, concurrentQueue, ^(size_t i){
NSLog(@"correct %zu",i);
//do something hard
});
}
5.dispatch_barrier_async解決多線程對同一資源進(jìn)行讀寫的沖突問題
__block NSString *str = @"1";
dispatch_queue_t dataQueue = dispatch_queue_create("com.starming.gcddemo.dataqueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(dataQueue, ^{
NSLog(@"%@",str);
});
dispatch_async(dataQueue, ^{
NSLog(@"%@",str);
});
//等待前面的都完成,在執(zhí)行barrier后面的
dispatch_barrier_async(dataQueue, ^{
str = @"2";
NSLog(@"%@",str);
});
dispatch_async(dataQueue, ^{
NSLog(@"%@",str);
});
dispatch_async(dataQueue, ^{
NSLog(@"%@",str);
});
dispatch_barrier_async
保證了同一隊列中,先提交的任務(wù)執(zhí)行完畢后才執(zhí)行自己提交的block辆苔。并且提交的block執(zhí)行完畢后荐吵,隊列會恢復(fù)成以前的樣子。
dispatch_barrier_async只在自己創(chuàng)建的隊列上有這種作用遥倦,在全局并發(fā)隊列和串行隊列上,效果和dispatch_sync一樣
6. dispatch_group線程組
dispatch groups是專門用來監(jiān)視多個異步任務(wù)堡称。dispatch_group_t實(shí)例用來追蹤不同隊列中的不同任務(wù)胎撤。
最常見的例子就是巫俺,多個異步并發(fā)任務(wù)執(zhí)行完畢后再執(zhí)行另外的操作。比如上傳一組圖片,或者下載多個文件赶撰。希望在全部完成時給用戶一個提示瘤载。
當(dāng)group里所有事件都完成GCD API有兩種方式發(fā)送通知墨技,
- 第一種是dispatch_group_wait,會阻塞當(dāng)前進(jìn)程,等所有任務(wù)都完成或等待超時。
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
//在group中添加隊列的block
dispatch_group_async(group, concurrentQueue, ^{
[NSThread sleepForTimeInterval:2.f];
NSLog(@"1");
});
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"2");
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"go on");
- 第二種方法是使用dispatch_group_notify,異步執(zhí)行閉包学搜,不會阻塞。
dispatch_group_t serviceGroup = dispatch_group_create();
// 開始第一個請求
// 進(jìn)入組
dispatch_group_enter(serviceGroup);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[NSThread sleepForTimeInterval:1.f];
NSLog(@"1");
dispatch_group_leave(serviceGroup);
});
// 開始第二個請求
// 先進(jìn)入組
dispatch_group_enter(serviceGroup);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
dispatch_group_leave(serviceGroup);
});
// 當(dāng)小組里的任務(wù)都清空以后,通知主線程完成了所有任務(wù)
dispatch_group_notify(serviceGroup,dispatch_get_main_queue(),^{
// Assess any errors
NSLog(@"finish");
});
NSLog(@"go on");
此處會先打印go on。
7.使用信號量Dispatch Semaphore
dispatch_semaphore_t 類似信號量,可以用來控制訪問某一資源訪問數(shù)量。
- dispatch_semaphore_create 初始化一個信號量毅桃,并設(shè)置它最大訪問數(shù)量读宙。
- dispatch_semaphore_wait 向某個信號量對象發(fā)送等待信號掖棉,信號量-1察纯,設(shè)置該次等待的時間,使用
DISPATCH_TIME_FOREVER
可以表示永久等待针肥。 - dispatch_semaphore_signal 向某個信號量發(fā)送釋放信號饼记,信號量+1。
當(dāng)信號量計數(shù)沒有超過最大訪問數(shù)量時慰枕,資源區(qū)可以被多個線程同時訪問具则。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
__block NSString *strTest = @"test";
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"--%@--1-", strTest);
dispatch_semaphore_signal(semaphore);
});
dispatch_async(concurrentQueue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"--%@--2-", strTest);
dispatch_semaphore_signal(semaphore);
});
dispatch_async(concurrentQueue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"--%@--3-", strTest);
dispatch_semaphore_signal(semaphore);
});
dispatch_async(concurrentQueue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"--%@--4-", strTest);
dispatch_semaphore_signal(semaphore);
});
打印結(jié)果:1掘猿,2唧龄,3眼姐,4.
解析:并發(fā)隊列只能保證按提交順序執(zhí)行律想,本來是不能保證執(zhí)行結(jié)果順序的,但在這里我們使用了dispatch_semaphore_t
限制了資源的訪問,由于最大只允許1個資源訪問川梅,所以這段代碼一定是按順序執(zhí)行的。
8.Dispatch Source監(jiān)聽進(jìn)程事件
建議閱讀iOS多線程——Dispatch Source
我試了一下里面的幾個例子苦掘,對Dispatch Source有了一定的理解慎颗,但沒有在開發(fā)中實(shí)際使用,等后面有實(shí)際經(jīng)驗再記錄分享营搅。
細(xì)說GCD(Grand Central Dispatch)如何用
iOS多線程GCD簡介(一)
iOS多線程GCD簡介(二)
GCD 深入理解:第一部分