以前總是被并行、串行贮匕,同步姐仅、異步 隊列搞得暈頭轉(zhuǎn)向,最近理順了一遍GCD的用法,再也不迷路了掏膏。
一劳翰、Dispatch Queue 是什么?
執(zhí)行處理的等待隊列馒疹,簡單點(diǎn)說就是:要做一件事情先給開條路給做好準(zhǔn)備佳簸,做事情的時候直接處理事情。
Dispatch Queue 按照追加的順序(先進(jìn)先出 FIFO)執(zhí)行處理行冰。
二溺蕉、 Dispatch Queue 隊列種類
1、Serial Dispatch Queue 等待現(xiàn)在執(zhí)行中處理結(jié)束 等待執(zhí)行 (串行)
2悼做、Concurent Dispatch Queue 不等待現(xiàn)在執(zhí)行中處理結(jié)束 立即執(zhí)行 (并行)
三 疯特、創(chuàng)建一個 Serial Dispatch Queue 隊列,順序執(zhí)行任務(wù)肛走。
NSLog(@"begin");
// Serila Disapatch Queue 串行隊列主要用在多個線程同事更新相同資源導(dǎo)致數(shù)據(jù)競爭時漓雅。
// 第一個參數(shù)是進(jìn)程的標(biāo)識符 Apple 推薦寫法為 使用應(yīng)用程序ID這種逆序全程域名
// DISPATCH_QUEUE_SERIAL 表示隊列類型為 串行隊列 等待上個任務(wù)執(zhí)行完成 再往下執(zhí)行。
dispatch_queue_t queue = dispatch_queue_create("queueSerial", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
dispatch_async(queue, ^{
NSLog(@"3");
});
dispatch_async(queue, ^{
NSLog(@"4");
});
NSLog(@"end");
執(zhí)行順序為:
2018-04-26 10:45:33.304185+0800 GCD[1118:74823] begin
2018-04-26 10:45:33.304396+0800 GCD[1118:74823] end
2018-04-26 10:45:33.304408+0800 GCD[1118:74869] 1
2018-04-26 10:45:33.304573+0800 GCD[1118:74869] 2
2018-04-26 10:45:33.304710+0800 GCD[1118:74869] 3
2018-04-26 10:45:33.305090+0800 GCD[1118:74869] 4
結(jié)果很明顯: 開始后新建了一個子線程queue朽色, 主線程和子線程同時往下執(zhí)行邻吞,主線程執(zhí)行NSLog 輸出@“end”,子線程按照順序執(zhí)行任務(wù)葫男,輸出 1 2 3 4 抱冷。
四、創(chuàng)建一個 Concurrent Dispatch Queue 隊列梢褐,并發(fā)執(zhí)行任務(wù)旺遮。
Concurrent Dispatch Queue 會立即執(zhí)行1 ,不等1執(zhí)行完 就開始執(zhí)行2 盈咳,不等2執(zhí)行完就開始執(zhí)行3耿眉,不等3執(zhí)行完再回到
NSLog(@"begin");
// 并行 第一個參數(shù) 是線程標(biāo)識符 第二個參數(shù)表示創(chuàng)建的是并行隊列 立即執(zhí)行
dispatch_queue_t queue =dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"111111--- %d----%@", i, [NSThread currentThread]);
};
});
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"222222--- %d----%@", i, [NSThread currentThread]);
};
});
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"333333--- %d----%@", i, [NSThread currentThread]);
};
});
NSLog(@"end");
結(jié)果為:
GCD[1489:125490] begin
GCD[1489:125490] end
GCD[1489:125581] 222222--- 0----<NSThread: 0x604000265480>{number = 4, name = (null)}
GCD[1489:125580] 111111--- 0----<NSThread: 0x604000265400>{number = 3, name = (null)}
GCD[1489:125578] 333333--- 0----<NSThread: 0x604000265580>{number = 5, name = (null)}
GCD[1489:125581] 222222--- 1----<NSThread: 0x604000265480>{number = 4, name = (null)}
GCD[1489:125580] 111111--- 1----<NSThread: 0x604000265400>{number = 3, name = (null)}
GCD[1489:125578] 333333--- 1----<NSThread: 0x604000265580>{number = 5, name = (null)}
GCD[1489:125581] 222222--- 2----<NSThread: 0x604000265480>{number = 4, name = (null)}
GCD[1489:125580] 111111--- 2----<NSThread: 0x604000265400>{number = 3, name = (null)}
GCD[1489:125578] 333333--- 2----<NSThread: 0x604000265580>{number = 5, name = (null)}
Concurrent Dispatch Queue 急性子 不管你有沒有做完前邊的事情,我反正要立即執(zhí)行鱼响,會開啟多個線程完成這個事情鸣剪。
這是最基本的兩種用法。
剛剛是自己創(chuàng)建Queue 丈积,其實我們也可以使用系統(tǒng)的Queue筐骇,
五、系統(tǒng)Serial (串行)隊列 dispatch_get_main_queue() 用法同三
/**
系統(tǒng)
主隊列
是Serial 類型
*/
dispatch_queue_t mainQueue = dispatch_get_main_queue();
六桶癣、系統(tǒng)Concurrent(并行) 隊列 dispatch_get_global_queue(0, 0) 用法同四
/**
系統(tǒng)
全局隊列
是Concurrent 類型
第一個參數(shù)是優(yōu)先級
DISPATCH_QUEUE_PRIORITY_HIGH 最高優(yōu)先
DISPATCH_QUEUE_PRIORITY_DEFAULT 默認(rèn)優(yōu)先級 0
DISPATCH_QUEUE_PRIORITY_LOW 低
DISPATCH_QUEUE_PRIORITY_BACKGROUND 后臺
*/
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
還有一些其他函數(shù)例如 :
七拥褂、柵欄:dispatch_barrier_async()<用來控制隊列的執(zhí)行先完成barrier 前的任務(wù)在完成barrier后的任務(wù)>
// 柵欄函數(shù)不能在 全局主線程中執(zhí)行
// 柵欄函數(shù)不能在 全局主線程中執(zhí)行
dispatch_queue_t queue = dispatch_queue_create("barrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"11111%d-----%@", i,[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"22222%d-----%@", i,[NSThread currentThread]);
}
});
dispatch_barrier_async(queue, ^{
NSLog(@"柵欄");
});
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"33333%d-----%@", i,[NSThread currentThread]);
}
});
輸出結(jié)果為:
GCD[2617:238165] 111110-----<NSThread: 0x60400027c100>{number = 3, name = (null)}
GCD[2617:238164] 222220-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238165] 111111-----<NSThread: 0x60400027c100>{number = 3, name = (null)}
GCD[2617:238164] 222221-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238165] 111112-----<NSThread: 0x60400027c100>{number = 3, name = (null)}
GCD[2617:238164] 222222-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238164] 柵欄
GCD[2617:238164] 333330-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238164] 333331-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238164] 333332-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
八、稍等:dispatch_after() <在子線程中過一段時間執(zhí)行某個任務(wù)>
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"過2s后執(zhí)行");
});
九牙寞、快速迭代:dispatch_apply() Demo 是from文件內(nèi)容轉(zhuǎn)移到to文件
// 會阻塞主線程進(jìn)行
NSLog(@"begin");
NSString *from = @"/Users/youName/Desktop/from";
NSString *to = @"/Users/youName/Desktop/to";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *fileArray = [fileManager subpathsAtPath:from];
/**
第一個參數(shù) :迭代次數(shù)
第二個參數(shù) :所在的queue
第三個參數(shù) :執(zhí)行的任務(wù)
*/
dispatch_apply([fileArray count], dispatch_queue_create("apply", DISPATCH_QUEUE_CONCURRENT), ^(size_t index) {
sleep(2);
NSLog(@"%zd ----%@",index, [NSThread currentThread]);
NSString *fromPath = [from stringByAppendingPathComponent:fileArray[index]];
NSString *toPath = [to stringByAppendingPathComponent:fileArray[index]];
[fileManager moveItemAtPath:fromPath toPath:toPath error:nil];
});
NSLog(@"end");
輸出結(jié)果為:可以看到apply會阻塞主線程進(jìn)行饺鹃。
2018-04-26 13:33:43.071251+0800 GCD[2735:248799] begin
2018-04-26 13:33:45.073933+0800 GCD[2735:248896] 2 ----<NSThread: 0x600000275bc0>{number = 3, name = (null)}
2018-04-26 13:33:45.073933+0800 GCD[2735:248799] 0 ----<NSThread: 0x6000000634c0>{number = 1, name = main}
2018-04-26 13:33:45.073949+0800 GCD[2735:248895] 3 ----<NSThread: 0x604000263580>{number = 5, name = (null)}
2018-04-26 13:33:45.074026+0800 GCD[2735:248898] 1 ----<NSThread: 0x6040002634c0>{number = 4, name = (null)}
2018-04-26 13:33:47.077143+0800 GCD[2735:248799] 4 ----<NSThread: 0x6000000634c0>{number = 1, name = main}
2018-04-26 13:33:47.078235+0800 GCD[2735:248799] end
十莫秆、隊列組 dispatch_group_t group = dispatch_group_create();<保證所有的任務(wù)都已經(jīng)完成最后在執(zhí)行某個任務(wù)>
// demo 是兩張圖片下載完成后再合成一張圖片
// 隊列組 是保證任務(wù)都已經(jīng)完成后在執(zhí)行某個任務(wù)。
dispatch_group_t group = dispatch_group_create(); // 創(chuàng)建隊列組
__block UIImage *image1 = [[UIImage alloc] init];
__block UIImage *image2 = [[UIImage alloc] init];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
imageView.contentMode = UIViewContentModeScaleAspectFill;
[self.view addSubview:imageView];
NSLog(@"begin");
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
NSString *imagePath =@"http://h.hiphotos.baidu.com/image/pic/item/63d0f703918fa0ecf70575602a9759ee3c6ddb99.jpg";
image1 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imagePath]]];
NSLog(@"任務(wù)1完成");
});
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
NSString *imagePath =@"http://d.hiphotos.baidu.com/image/pic/item/8435e5dde71190ef3ddc94b7c21b9d16fdfa60b6.jpg";
image2 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imagePath]]];
NSLog(@"任務(wù)2完成");
});
// 所有任務(wù)執(zhí)行完成后才會執(zhí)行這個任務(wù)悔详。
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"所有任務(wù)執(zhí)行完畢開始最后合成圖片操作");
// 拼接兩張圖片
// 開啟圖形上下文
UIGraphicsBeginImageContext(CGSizeMake([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height));
// 畫1
[image1 drawInRect:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.width * 0.4)];
// 畫2
[image2 drawInRect:CGRectMake(0, [UIScreen mainScreen].bounds.size.height *0.3, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height * 0.7)];
// 得到繪制好的圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 關(guān)閉圖形上下文
// 回到主線程刷新UI
NSLog(@"合成完畢");
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = image;
NSLog(@"回到主線程刷新UI");
});
});
NSLog(@"end");
輸出結(jié)果為:可以看到group會等任務(wù)都完成后再做最后的操作镊屎。
2018-04-26 13:49:46.539181+0800 GCD[2910:273617] begin
2018-04-26 13:49:46.539414+0800 GCD[2910:273617] end
2018-04-26 13:49:48.905053+0800 GCD[2910:273683] 任務(wù)2完成
2018-04-26 13:49:48.905053+0800 GCD[2910:273679] 任務(wù)1完成
2018-04-26 13:49:48.905325+0800 GCD[2910:273617] 所有任務(wù)執(zhí)行完畢開始最后合成圖片操作
2018-04-26 13:49:48.941813+0800 GCD[2910:273617] 合成完畢
2018-04-26 13:49:48.942956+0800 GCD[2910:273617] 回到主線程刷新UI
十一、單例 dispatch_once()
// 在touchBegin 調(diào)用此方法
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"只會執(zhí)行一次 適合全局單例對象使用");
});
NSLog(@"別點(diǎn)了茄螃,就會執(zhí)行一次");
輸出結(jié)果為:
2018-04-26 13:53:42.167590+0800 GCD[2974:280429] 只會執(zhí)行一次 適合全局單例對象使用
2018-04-26 13:53:42.167835+0800 GCD[2974:280429] 別點(diǎn)了缝驳,就會執(zhí)行一次
2018-04-26 13:53:42.523634+0800 GCD[2974:280429] 別點(diǎn)了,就會執(zhí)行一次
2018-04-26 13:53:42.685084+0800 GCD[2974:280429] 別點(diǎn)了归苍,就會執(zhí)行一次
還有一些其他的用用法我還沒掌握用狱,歡迎各位大牛批評指正。
Demo 地址:https://github.com/MYLILUYANG/GCD-
更加詳細(xì)總結(jié): http://www.cocoachina.com/ios/20180313/22573.html