GCD的基本使用
需要經(jīng)常使用的兩個(gè)函數(shù)和隊(duì)列
- 同步函數(shù)
dispatch_sync:立刻馬上執(zhí)行,如果沒(méi)有執(zhí)行完畢,后面的也不會(huì)執(zhí)行- 異步函數(shù)
dispatch_async:如果沒(méi)有執(zhí)行完畢,后面的也可以執(zhí)行- 并發(fā)隊(duì)列
DISPATCH_QUEUE_CONCURRENT- 串行隊(duì)列
DISPATCH_QUEUE_SERIAL
創(chuàng)建隊(duì)列方法
- 方法1:自己創(chuàng)建一個(gè)新的線程
/**
第一個(gè)參數(shù): C語(yǔ)言的字符串,標(biāo)簽
第二個(gè)參數(shù): 隊(duì)列的類型
DISPATCH_QUEUE_CONCURRENT:并發(fā)隊(duì)列
DISPATCH_QUEUE_SERIAL:串行隊(duì)列
*/
dispatch_queue_t queue = dispatch_queue_create("asyncConcurrent", DISPATCH_QUEUE_CONCURRENT);
- 方法二:獲得全局并發(fā)隊(duì)列 且并不是一個(gè)任務(wù)一條線程,是系統(tǒng)隨機(jī)給你分配線程
// 獲得全局并發(fā)隊(duì)列:并不是一個(gè)任務(wù)一條線程,而是系統(tǒng)給你分配的線程
/**
第一個(gè)參數(shù): 優(yōu)先級(jí)
第二個(gè)參數(shù): 未來(lái)使用的,默認(rèn) 0
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
封裝任務(wù)方法
/**
第一個(gè)參數(shù): 隊(duì)列
第二個(gè)參數(shù): 要執(zhí)行的任務(wù)
*/
- 方法一:異步函數(shù)
dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
- 方法二:同步函數(shù)
dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
異步函數(shù)+并發(fā)隊(duì)列
會(huì)開(kāi)啟多條線程,隊(duì)列中的任務(wù)是并發(fā)(異步)執(zhí)行 無(wú)順序
// 異步函數(shù)+并發(fā)隊(duì)列:會(huì)開(kāi)啟多條線程,隊(duì)列中的任務(wù)是并發(fā)(異步)執(zhí)行無(wú)順序
- (void) asyncConcurrent{
// 1.創(chuàng)建隊(duì)列
/**
第一個(gè)參數(shù): C語(yǔ)言的字符串,標(biāo)簽
第二個(gè)參數(shù): 隊(duì)列的類型
DISPATCH_QUEUE_CONCURRENT:并發(fā)隊(duì)列
DISPATCH_QUEUE_SERIAL:串行隊(duì)列
*/
// dispatch_queue_t queue = dispatch_queue_create("asyncConcurrent", DISPATCH_QUEUE_CONCURRENT);
// 獲得全局并發(fā)隊(duì)列:并不是一個(gè)任務(wù)一條線程,而是系統(tǒng)給你分配的線程
/**
第一個(gè)參數(shù): 優(yōu)先級(jí)
第二個(gè)參數(shù): 未來(lái)使用的,默認(rèn) 0
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2 .封裝任務(wù) -> 添加任務(wù)到隊(duì)列中
/**
第一個(gè)參數(shù): 隊(duì)列
第二個(gè)參數(shù): 要執(zhí)行的任務(wù)
*/
dispatch_async(queue, ^{
NSLog(@"asyncConcurrent1------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"asyncConcurrent2------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"asyncConcurrent3------%@",[NSThread currentThread]);
});
}
異步?函數(shù)+串行隊(duì)列
只會(huì)開(kāi)啟一條線程,任務(wù)是串行執(zhí)行有順序
// 異步?函數(shù)+串行隊(duì)列:只會(huì)開(kāi)啟一條線程,任務(wù)是串行執(zhí)行有順序
- (void) asyncSerial{
//1. 創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("asyncSerial", DISPATCH_QUEUE_SERIAL);
// 2. 封裝任務(wù)
dispatch_async(queue, ^{
NSLog(@"asyncSerial1------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"asyncSerial2------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"asyncSerial3------%@",[NSThread currentThread]);
});
}
同步函數(shù)+并發(fā)隊(duì)列
不會(huì)開(kāi)啟線程,任務(wù)是串行執(zhí)行
// 同步函數(shù)+并發(fā)隊(duì)列:不會(huì)開(kāi)啟線程,任務(wù)是串行執(zhí)行
- (void) syncConcurrent{
// 1. 創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("syncConcurrent", DISPATCH_QUEUE_CONCURRENT);
// 2. 封裝任務(wù)
dispatch_sync(queue, ^{
NSLog(@"syncConcurrent1------%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"syncConcurrent2------%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"syncConcurrent3------%@",[NSThread currentThread]);
});
}
同步函數(shù)+串行隊(duì)列
不會(huì)開(kāi)啟線程,任務(wù)是串行執(zhí)行
// 同步函數(shù)+串行隊(duì)列:不會(huì)開(kāi)啟線程,任務(wù)是串行執(zhí)行
- (void) syncSerial{
// 1. 創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("syncSerial", DISPATCH_QUEUE_SERIAL);
// 2. 封裝任務(wù)
dispatch_sync(queue, ^{
NSLog(@"syncSerial1------%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"syncSerial2------%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"syncSerial3------%@",[NSThread currentThread]);
});
}
特別的隊(duì)列----主隊(duì)列
特點(diǎn):如果主隊(duì)列中發(fā)現(xiàn)當(dāng)前主線程有任務(wù)在執(zhí)行,那么主隊(duì)列會(huì)暫停調(diào)用隊(duì)列中的任務(wù),直到主線程空閑為止
異步函數(shù)+主隊(duì)列
所有任務(wù)都在主線程中執(zhí)行,不會(huì)開(kāi)啟線程
- (void) asyncMain{
// 1. 獲得主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.封裝任務(wù) - 異步函數(shù)
dispatch_async(queue, ^{
NSLog(@"asyncMain1------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"asyncMain2------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"asyncMain3------%@",[NSThread currentThread]);
});
}
同步函數(shù)+主隊(duì)列
死鎖 如果該方法在子線程中執(zhí)行,那么所有的任務(wù)都會(huì)在主線程中執(zhí)行
// 同步函數(shù)+主隊(duì)列:死鎖
// 注意:如果該方法在子線程中執(zhí)行,那么所有的任務(wù)都會(huì)在主線程中執(zhí)行
- (void) syncMain{
// 1. 獲得主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.封裝任務(wù) - 同步函數(shù)
dispatch_sync(queue, ^{
NSLog(@"syncMain1------%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"syncMain2------%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"syncMain3------%@",[NSThread currentThread]);
});
}
備注
dispatch_async和dispatch_async_f的區(qū)別在于它們封裝任務(wù)的方法,dispatch_async使用的是block而dispatch_async_f使用的則是C語(yǔ)言函數(shù),其它是沒(méi)有任何區(qū)別的,鑒于_f用得比較少 這里只做簡(jiǎn)單的了解
- (void) note{
// 創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 區(qū)別:封裝任務(wù)的方法(block 函數(shù))
int context = 10;
/**
* 第一個(gè)參數(shù):隊(duì)列
第二個(gè)參數(shù):參數(shù) 函數(shù)指針 注意傳地址 不要傳值
第三個(gè)參數(shù):要調(diào)用函數(shù)名稱 C語(yǔ)言函數(shù)
*/
dispatch_async_f(queue, &context, task);
}
void task (void * context){
int * c = context;
NSLog(@"%d",*c);
}
GCD線程間的通信
這里以下載網(wǎng)絡(luò)圖片為例子
- (void) downloadImage{
// 創(chuàng)建子線程下載圖片
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 1. 確定圖片url
NSURL * url = [NSURL URLWithString:@"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1909600733,2304577724&fm=26&gp=0.jpg"];
// 2.下載圖片二進(jìn)制數(shù)據(jù)到本地
NSData * imageData = [NSData dataWithContentsOfURL:url];
// 3.轉(zhuǎn)換圖片
UIImage * image = [UIImage imageWithData:imageData];
NSLog(@"當(dāng)前線程------%@",[NSThread currentThread]);
// 更新UI 回到主線程
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
NSLog(@"當(dāng)前線程------%@",[NSThread currentThread]);
});
});
}
GCD的常用函數(shù)
延遲執(zhí)行
dispatch_afterGCD 可以自由選擇在哪個(gè)線程執(zhí)行
// 方法3. GCD 可以自由選擇在哪個(gè)線程執(zhí)行
// dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
/**
* 第一個(gè)參數(shù):DISPATCH_TIME_NOW 從現(xiàn)在開(kāi)始計(jì)算時(shí)間
第一個(gè)參數(shù):延遲2.0 GCD時(shí)間單位:納秒 (需要 * 10的9次方 轉(zhuǎn)換為秒)
*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
NSLog(@"GCD延遲執(zhí)行------%@",[NSThread currentThread]);
NSLog(@"------GCDend------");
});
一次性代碼
dispatch_once注意:不能夠在懶加載中使用
// 一次性代碼
- (void) once{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"------noce------");
});
}
柵欄函數(shù)
dispatch_barrier_async當(dāng)之前的任務(wù)執(zhí)行完成之后才會(huì)執(zhí)行后面的任務(wù) 柵欄函數(shù)不能使用全局并發(fā)隊(duì)列
// 1 獲取全局并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);
// 1. 異步函數(shù)
dispatch_async(queue, ^{
NSLog(@"download1------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"download3------%@",[NSThread currentThread]);
});
// 柵欄函數(shù)
// 柵欄函數(shù)不能使用全局并發(fā)隊(duì)列
dispatch_barrier_async(queue, ^{
NSLog(@"+++++++++++++++++++++");
});
dispatch_async(queue, ^{
NSLog(@"download2------%@",[NSThread currentThread]);
});
GCD的快速迭代
dispatch_apply因?yàn)樵诘倪^(guò)程中會(huì)開(kāi)啟子線程 所以比一般的for循環(huán)迭代要快速很多
// GCD快速迭代 :開(kāi)啟
- (void) apply{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
/**
* 第一個(gè)參數(shù):遍歷次數(shù)
第二個(gè)參數(shù):隊(duì)列(并發(fā)隊(duì)列)
第三個(gè)參數(shù):索引
*/
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%zd-----------%@",index,[NSThread currentThread]);
});
}
下面通過(guò)一個(gè)文件剪切的Demo來(lái)演示??
- (void) moveFileWithGCD{
// 1. 拿到文件路徑
NSString * form = @"/Users/ChangRJey/Desktop/修改前后對(duì)比圖";
// 2. 獲得目標(biāo)文件路徑
NSString * to = @"/Users/ChangRJey/Desktop/yoyoyo";
// 3. 得到目錄下面的所有文件
NSArray * subPaths= [[NSFileManager defaultManager] subpathsAtPath:form];
NSLog(@"文件名------%@",subPaths);
// 4. 遍歷所有文件,然后執(zhí)行剪切操作
NSInteger count = subPaths.count;
dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t index) {
NSString * fullPath = [form stringByAppendingPathComponent:subPaths[index]];
NSString * toFullPath = [to stringByAppendingPathComponent:subPaths[index]];
NSLog(@"文件名------%@",fullPath);
// 4.2 執(zhí)行剪切操作
/**
* 第一個(gè)參數(shù):要剪切的文件路徑
第二個(gè)參數(shù):文件應(yīng)該被存放的目標(biāo)路徑
第三個(gè)參數(shù):錯(cuò)誤信息
*/
[[NSFileManager defaultManager] moveItemAtPath:fullPath toPath:toFullPath error:nil];
NSLog(@"%@------%@------%@",fullPath,toFullPath,[NSThread currentThread]);
});
}
GCD隊(duì)列組的基本使用
創(chuàng)建Group
dispatch_group_t
完成通知
dispatch_group_notify
- 方法一 簡(jiǎn)化版
// 攔截通知,當(dāng)隊(duì)列組中所有任務(wù)都執(zhí)行完成完畢之后會(huì)進(jìn)行通知
// 內(nèi)部本身是異步的
- (void) groupOne{
// 1. 創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2. 創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
// 3. 異步函數(shù)封裝任務(wù)
dispatch_group_async(group, queue, ^{
NSLog(@"download_group1------%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"download_group2------%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"download_group3------%@",[NSThread currentThread]);
});
// 攔截通知,當(dāng)隊(duì)列組中所有任務(wù)都執(zhí)行完成完畢之后會(huì)進(jìn)行通知
// 內(nèi)部本身是異步的
dispatch_group_notify(group, queue, ^{
NSLog(@"------dispatch_group_notify------");
});
}
- 方法二 以前版本
- (void) groupTwo{
// 1. 創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2. 創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
// 3. 在該方法后面的異步任務(wù)會(huì)被納入到隊(duì)列組的監(jiān)聽(tīng)范圍
// dispatch_group_enter | dispatch_group_leave 必須配對(duì)使用
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"download_group1------%@",[NSThread currentThread]);
// 離開(kāi)隊(duì)列組
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"download_group2------%@",[NSThread currentThread]);
// 離開(kāi)隊(duì)列組
dispatch_group_leave(group);
});
// dispatch_group_notify(group, queue, ^{
// NSLog(@"------dispatch_group_notify------");
// });
// 等待
// DISPATCH_TIME_FOREVER:死等,知道隊(duì)列組中所有任務(wù)執(zhí)行完畢之后才能執(zhí)行 本身是阻塞的
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"------end------");
}
示例
下面通過(guò)一個(gè)例子來(lái)實(shí)際使用GCD的隊(duì)列組 通過(guò)下載 圖片1 和 圖片2 之后然后合并兩張圖片 因?yàn)楹喜蓮垐D片必須得讓子線程中的 圖片1 和 圖片2 下載完成之后才能夠進(jìn)行合并圖片
- (void) groupThree{
/**
* 1. 下載圖片1 開(kāi)子線程
2. 下載圖片2 開(kāi)子線程
3. 合成圖片并顯示圖片 開(kāi)子線程
*/
// 獲得并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
// 1. 下載圖片1 開(kāi)子線程
dispatch_group_async(group, queue, ^{
NSLog(@"download_image1------%@",[NSThread currentThread]);
// 1. 1 確定url
NSURL * url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1502282744935&di=aaefb03a83917b6b94da36fbf69fa080&imgtype=0&src=http%3A%2F%2Fpic.ilitu.com%2Fy3%2F1475_39900960921.jpg"];
// 1.2 下載圖片二進(jìn)制數(shù)據(jù)到本地
NSData * imageData = [NSData dataWithContentsOfURL:url];
// 1.3 轉(zhuǎn)換圖片
self.image1 = [UIImage imageWithData:imageData];
});
// 2. 下載圖片1 開(kāi)子線程
dispatch_group_async(group, queue, ^{
NSLog(@"download_image2------%@",[NSThread currentThread]);
// 2. 1 確定url
NSURL * url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1502282773716&di=9b788f0dda94c259067c2826795caef4&imgtype=0&src=http%3A%2F%2Fi3.sinaimg.cn%2Fgm%2F2015%2F0422%2FU3932P115DT20150422122125.jpg"];
// 2.2 下載圖片二進(jìn)制數(shù)據(jù)到本地
NSData * imageData = [NSData dataWithContentsOfURL:url];
// 2.3 轉(zhuǎn)換圖片
self.image2 = [UIImage imageWithData:imageData];
});
// 3. 合成圖片并顯示圖片 開(kāi)子線程
dispatch_group_notify(group, queue, ^{
NSLog(@"合并圖片------%@",[NSThread currentThread]);
// 3.1 創(chuàng)建圖形上下文
UIGraphicsBeginImageContext(CGSizeMake(300, 400));
// 3.2 畫(huà)圖1
[self.image1 drawInRect:CGRectMake(0, 0, 300, 200)];
self.image1 = nil;
// 3.3 畫(huà)圖2
[self.image2 drawInRect:CGRectMake(0, 200, 300, 200)];
self.image2 = nil;
// 3.4 根據(jù)上下文得到一張圖片
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
// 3.5 關(guān)閉上下文
UIGraphicsEndImageContext();
// 3.6 得到圖片 回到主線程刷新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
NSLog(@"更新UI------%@",[NSThread currentThread]);
});
});
}
以上就是GCD所有的基本常用方法功能,希望能夠幫助到大家,所有Demo我已經(jīng)上傳到GitHub,有喜歡的朋友記得給個(gè)Stare,謝謝!
另外分享一個(gè)覺(jué)得好用的已經(jīng)封裝好的GCD
我是Renjiee 我要做最騷的程序猿????????????