1.GCD
GCD(Grand Central Dispatch) 偉大的中樞調(diào)度器
GCD是蘋果為多核的并行運(yùn)算提出的方案
GCD會充分利用CPU內(nèi)核,會自動管理自己的生命周期
GCD 函數(shù)分為同步和異步
同步函數(shù)為 dispatch_sync
異步函數(shù)為 dispatch_async
1.1 核心概念
GCD 分為任務(wù),隊(duì)列
1.1.1.定制任務(wù)
1.1.2.將任務(wù)添加到隊(duì)列中(GCD會自動將隊(duì)列中的任務(wù)取出,放在對應(yīng)的線程來執(zhí)行)
1.2 隊(duì)列類型
1.并發(fā)和串行
1.1 并發(fā)是指多個任務(wù)同時進(jìn)行(自動開啟多個線程執(zhí)行)
1.2 并發(fā)功能只能在異步函數(shù)中進(jìn)行
2.串行隊(duì)列
讓任務(wù)一個接一個來執(zhí)行(上個任務(wù)完成后才接著進(jìn)行下一個)
2.容易混淆的術(shù)語
同步和異步 : 主要區(qū)別是,是否具有開線程的能力
并發(fā)和串行 : 這兩個是隊(duì)列,是執(zhí)行任務(wù)的方式
3 .GCD常用的幾種方法
3.1 異步并行(這種方式可以開啟線程,同時執(zhí)行多個任務(wù))
#pragma mark - 異步并行隊(duì)列
-(void)asyncConcurrent{
// 創(chuàng)建隊(duì)列
// 第一個參數(shù)是標(biāo)識,第二個是并行隊(duì)列
// dispatch_queue_t queue = dispatch_queue_create("cnw", DISPATCH_QUEUE_CONCURRENT);
// 創(chuàng)建全局隊(duì)列,第一個參數(shù)是隊(duì)列的優(yōu)先級,這個是獲得隊(duì)列,就已經(jīng)存在
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 封裝任務(wù),把任務(wù)放在隊(duì)列中執(zhí)行
dispatch_async(queue, ^{
NSLog(@"1-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
}
3.2 異步串行(可以開啟線程,不過是開啟一條線程,任務(wù)有序執(zhí)行)
#pragma mark - 異步串行隊(duì)列
-(void)asyncSerial
{
// 創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("cnw0", DISPATCH_QUEUE_SERIAL);
// 封裝任務(wù)
dispatch_async(queue, ^{
NSLog(@"1-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
}
3.3 同步串行(這種方法不會開啟線程,任務(wù)有序執(zhí)行)
#pragma mark - 同步串行隊(duì)列
-(void)syncSerial
{
// 創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("cnw0", DISPATCH_QUEUE_SERIAL);
// 封裝任務(wù)
dispatch_sync(queue, ^{
NSLog(@"1-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
}
3.4 同步并行(不會開啟線程,任務(wù)有序執(zhí)行)
#pragma mark - 同步并行隊(duì)列
-(void)syncConcurrent
{
// 創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("cnw0", DISPATCH_QUEUE_CONCURRENT);
// 封裝任務(wù)
dispatch_sync(queue, ^{
NSLog(@"1-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
}
4.主隊(duì)列的特殊性
當(dāng)前隊(duì)列為主隊(duì)列時,異步執(zhí)行不會創(chuàng)建線程.
有主隊(duì)列時,執(zhí)行方法是在主線程中執(zhí)行的,不會在子線程中執(zhí)行
#pragma mark - 異步主隊(duì)列
-(void)asyncMain
{
dispatch_queue_t queue = dispatch_get_main_queue();
// 封裝任務(wù),把任務(wù)放在隊(duì)列中執(zhí)行
dispatch_async(queue, ^{
NSLog(@"1-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
}
打印結(jié)果
2017-03-24 15:02:48.962 GCD[10228:591925] 1-------當(dāng)前隊(duì)列<NSThread: 0x60800006e100>{number = 1, name = main}
2017-03-24 15:02:48.962 GCD[10228:591925] 2-------當(dāng)前隊(duì)列<NSThread: 0x60800006e100>{number = 1, name = main}
2017-03-24 15:02:48.962 GCD[10228:591925] 3-------當(dāng)前隊(duì)列<NSThread: 0x60800006e100>{number = 1, name = main}
當(dāng)在同步函數(shù)中執(zhí)行主隊(duì)列時會出現(xiàn)死鎖
#pragma mark - 同步主隊(duì)列
-(void)syncMain
{
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"開始執(zhí)行");
// 封裝任務(wù),把任務(wù)放在隊(duì)列中執(zhí)行
dispatch_sync(queue, ^{
NSLog(@"1-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
});
NSLog(@"完成執(zhí)行");
}
原因是當(dāng)執(zhí)行主隊(duì)列的任務(wù)時,會在主線程執(zhí)行,而當(dāng)前是同步函數(shù),任務(wù)是一個完成后接著下一個,所以當(dāng)進(jìn)行第一個線程的時候,會在主線程執(zhí)行,而此時主線程在等這個任務(wù)完成.
主隊(duì)列特點(diǎn):如果主隊(duì)列發(fā)現(xiàn)當(dāng)前主線程有任務(wù)在執(zhí)行,那么主隊(duì)列會暫停調(diào)用隊(duì)列中的任務(wù),知道主線程空閑為止.
如果在子線程中執(zhí)行同步函數(shù)則可以有序執(zhí)行任務(wù)
最后附上一張表

- GCD線程間的通信
還是下載圖片,當(dāng)下載完圖片以后,回到主線程,只需要是主隊(duì)列就會在主線程執(zhí)行
這時候同步回到主線程是不會出現(xiàn)死鎖的,因?yàn)檫@一步操作是在子線程中操作的
#pragma mark - GCD線程間通信
-(void)threadMes
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
UIImage * image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1490352377321&di=57b814f647287d559e264311887c5e9c&imgtype=0&src=http%3A%2F%2Fimg.sj33.cn%2Fuploads%2Fallimg%2F201402%2F102J0KJ-24.jpg"]]];
// 加載好數(shù)據(jù)以后,在UI界面顯示回到主線程
// dispatch_sync(dispatch_get_main_queue()
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
NSLog(@"當(dāng)前線程是00%@",[NSThread currentThread]);
});
NSLog(@"當(dāng)前線程是01%@",[NSThread currentThread]);
});
}
6.GCD的兩個函數(shù)
6.1 延時操作
#pragma mark - GCD延時操作
-(void)delay
{
NSLog(@"開始任務(wù)");
// 第一種方法
// [self performSelector:@selector(run) withObject:self afterDelay:2.0];
// 第二種方法
// [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 第三種方法GCD
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
// 可以在主線程,也可以現(xiàn)在子線程
NSLog(@"%@",[NSThread currentThread]);
});
}
-(void)run
{
NSLog(@"%@",[NSThread currentThread]);
}
6.2 只會創(chuàng)建一次(創(chuàng)建單例時會用到)
#pragma mark - GCD只會創(chuàng)建一次的操作
-(void)once
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"打印了幾次");
});
}
7.柵欄函數(shù)
根據(jù)表面意思就知道,柵欄:攔截的意思,就是等上邊的任務(wù)執(zhí)行完成以后,才可以往下進(jìn)行
但柵欄函數(shù)的使用隊(duì)列,不能是全局隊(duì)列,不然是沒效果的
#pragma mark - 柵欄函數(shù)
-(void)railings
{
dispatch_queue_t queue = dispatch_queue_create("cnw01", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"thread1--------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"thread3--------%@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"thread2??--------%@",[NSThread currentThread]);
});
}
8 . GCD的快速迭代(剪切文件)
#pragma mark - 快速迭代
-(void)apply
{
// 首先獲得需要剪切的文件夾
NSString * frome = @"/Users/chenningwei/Desktop/屏幕截圖";
// 剪切到的文件夾
NSString * to = @"/Users/chenningwei/Desktop/目標(biāo)文件";
// 得到需要剪切文件夾內(nèi)的元素
NSArray * files = [[NSFileManager defaultManager] subpathsAtPath:frome];
// 遍歷數(shù)組中的元素
NSInteger count = files.count;
dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t index) {
// 獲得剪切文件內(nèi)文件的全路徑
NSString * fromePath = [frome stringByAppendingPathComponent:files[index]];
// 剪切到文件夾文件全路徑
NSString * toPath = [to stringByAppendingPathComponent:files[index]];
// 剪切
[[NSFileManager defaultManager] moveItemAtPath:fromePath toPath:toPath error:nil];
NSLog(@"%@----%@-----%@",fromePath,toPath,[NSThread currentThread]);
});
}
9 . 隊(duì)列組
隊(duì)列組執(zhí)行是可以等上邊操作完成以后,來進(jìn)行下一步的操作,這是一般的異步操作實(shí)現(xiàn)不了了(目前我的水平)
比如:下載兩張圖片后,進(jìn)行合成
只有兩張圖片下載完成以后才可以進(jìn)行合并,而普通的異步操作是無序的實(shí)現(xiàn)不了,就需要用到隊(duì)列組來實(shí)現(xiàn),下面看代碼
#pragma mark - 隊(duì)列組下載圖片
-(void)queues
{
// 創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
// 全局隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 下載第一張圖片
dispatch_group_async(group, queue, ^{
NSURL * url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1490352377321&di=57b814f647287d559e264311887c5e9c&imgtype=0&src=http%3A%2F%2Fimg.sj33.cn%2Fuploads%2Fallimg%2F201402%2F102J0KJ-24.jpg"];
NSData * data = [NSData dataWithContentsOfURL:url];
self.image1= [UIImage imageWithData:data];
});
dispatch_group_async(group, queue, ^{
NSURL * url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1490352377321&di=57b814f647287d559e264311887c5e9c&imgtype=0&src=http%3A%2F%2Fimg.sj33.cn%2Fuploads%2Fallimg%2F201402%2F102J0KJ-24.jpg"];
NSData * data = [NSData dataWithContentsOfURL:url];
self.image2= [UIImage imageWithData:data];
});
// 等兩張圖片下載完以后進(jìn)行合成
dispatch_group_notify(group, queue, ^{
// 開始圖形上下文
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
//畫圖
[self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];
self.image1 = nil;
[self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];
self.image2 = nil;
// 根據(jù)圖形上下文得到一張圖片
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
// 關(guān)閉圖形上下文
UIGraphicsEndPDFContext();
// 設(shè)置圖片
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView1.image = image;
});
});
}
10 . ARC環(huán)境下的單例模式
.1 首先定義一個全局變量
static Tool * _instance;
.2 重寫allocWithZone方法
+(instancetype)allocWithZone:(struct _NSZone *)zone
在這方法里創(chuàng)建單例
// 第一種懶加載方法
// 防止多條線程共享一塊資源出現(xiàn)的安全問題,加互斥鎖
@synchronized(self){
if ( _instance == nil ){
_instance = [super allocWithZone:zone];
}
}
return _instance;
// 第二種利用GCD創(chuàng)建
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
// 在.h文件中可以定義個類方法,供外界調(diào)用,在.m文件中的實(shí)現(xiàn)方法
+(instancetype)shareTool
{
return [[self alloc] init];
}
// 但是為了安全起見,會重寫
-(id)copyWithZone:(NSZone *)zone
{
return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
return _instance;
}