3.GCD中有2个核心概念
4.GCD的使用就2个步骤
/*
异步 + 并发 : 会开启新的线程
如果任务比较多, 那么就会开启多个线程
*/
- (void)asynConcurrent
{
/*
执行任务
dispatch_async
dispatch_sync
*/
/*
第一个参数: 队列的名称
第二个参数: 告诉系统需要创建一个并发队列还是串行队列
DISPATCH_QUEUE_SERIAL :串行
DISPATCH_QUEUE_CONCURRENT 并发
*/
// dispatch_queue_t queue = dispatch_queue_create("com.longshao.lsl", DISPATCH_QUEUE_CONCURRENT);
// 系统内部已经给我们提供好了一个现成的并发队列
/*
第一个参数: iOS8以前是优先级, iOS8以后是服务质量
iOS8以前
* - DISPATCH_QUEUE_PRIORITY_HIGH 高优先级 2
* - DISPATCH_QUEUE_PRIORITY_DEFAULT: 默认的优先级 0
* - DISPATCH_QUEUE_PRIORITY_LOW: 低优先级 -2
* - DISPATCH_QUEUE_PRIORITY_BACKGROUND:
iOS8以后
* - QOS_CLASS_USER_INTERACTIVE 0x21 用户交互(用户迫切想执行任务)
* - QOS_CLASS_USER_INITIATED 0x19 用户需要
* - QOS_CLASS_DEFAULT 0x15 默认
* - QOS_CLASS_UTILITY 0x11 工具(低优先级, 苹果推荐将耗时操作放到这种类型的队列中)
* - QOS_CLASS_BACKGROUND 0x09 后台
* - QOS_CLASS_UNSPECIFIED 0x00 没有设置
第二个参数: 废物
*/
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
/*
第一个参数: 用于存放任务的队列
第二个参数: 任务(block)
GCD从队列中取出任务, 遵循FIFO原则 , 先进先出
输出的结果和苹果所说的原则不符合的原因: CPU可能会先调度其它的线程
能够创建新线程的原因:
我们是使用"异步"函数调用
能够创建多个子线程的原因:
我们的队列是并发队列
*/
dispatch_async(queue, ^{
NSLog(@"任务1 == %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务2 == %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务3 == %@", [NSThread currentThread]);
});
}
/*
异步 + 串行:会开启新的线程
但是只会开启一个新的线程
注意: 如果调用 异步函数, 那么不用等到函数中的任务执行完毕, 就会执行后面的代码
*/
- (void)asynSerial
{
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.longshao.lsl", DISPATCH_QUEUE_SERIAL);
/*
能够创建新线程的原因:
我们是使用"异步"函数调用
只创建1个子线程的原因:
我们的队列是串行队列
*/
// 2.将任务添加到队列中
dispatch_async(queue, ^{
NSLog(@"任务1 == %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务2 == %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务3 == %@", [NSThread currentThread]);
});
NSLog(@"--------");
}
/*
同步 + 串行: 不会开启新的线程
注意点: 如果是调用 同步函数, 那么会等同步函数中的任务执行完毕, 才会执行后面的代码
*/
- (void)syncSerial
{
// 1.创建一个串行队列
// #define DISPATCH_QUEUE_SERIAL NULL
// 所以可以直接传NULL
dispatch_queue_t queue = dispatch_queue_create("com.longshao.lsl", NULL);
// 2.将任务添加到队列中
dispatch_sync(queue, ^{
NSLog(@"任务1 == %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务2 == %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务3 == %@", [NSThread currentThread]);
});
NSLog(@"---------");
}
/*
同步 + 并发 : 不会开启新的线程
妻管严
*/
- (void)syncConCurrent
{
// 1.创建一个并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2.将任务添加到队列中
dispatch_sync(queue, ^{
NSLog(@"任务1 == %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务2 == %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务3 == %@", [NSThread currentThread]);
});
NSLog(@"---------");
}
/*
异步 + 主队列 : 不会创建新的线程, 并且任务是在主线程中执行
*/
- (void)asyncMain
{
// 主队列:
// 特点: 只要将任务添加到主队列中, 那么任务"一定"会在主线程中执行 \
无论你是调用同步函数还是异步函数
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"%@", [NSThread currentThread]);
});
}
/*
如果是在主线程中调用同步函数 + 主队列, 那么会导致死锁
导致死锁的原因:
sync函数是在主线程中执行的, 并且会等待block执行完毕. 先调用
block是添加到主队列的, 也需要在主线程中执行. 后调用
*/
- (void)syncMain
{
NSLog(@"%@", [NSThread currentThread]);
// 主队列:
dispatch_queue_t queue = dispatch_get_main_queue();
// 如果是调用 同步函数, 那么会等同步函数中的任务执行完毕, 才会执行后面的代码
// 注意: 如果dispatch_sync方法是在主线程中调用的, 并且传入的队列是主队列, 那么会导致死锁
dispatch_sync(queue, ^{
NSLog(@"----------");
NSLog(@"%@", [NSThread currentThread]);
});
NSLog(@"----------");
}
/*
如果是在子线程中调用 同步函数 + 主队列, 那么没有任何问题
*/
- (void)syncMain2
{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
// block会在子线程中执行
// NSLog(@"%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
// block一定会在主线程执行
NSLog(@"%@", [NSThread currentThread]);
});
});
NSLog(@"------------");
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"--------");
// 1.除主队列以外, 随便搞一个队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2.调用异步函数
dispatch_async(queue, ^{
// 1.下载图片
NSURL *url = [NSURL URLWithString:@"http://pic.4j4j.cn/upload/pic/20130531/07ed5ea485.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
// 2.将二进制转换为图片
UIImage *image = [UIImage imageWithData:data];
// 3.回到主线程更新UI
// self.imageView.image = image;
/*
技巧:
如果想等UI更新完毕再执行后面的代码, 那么使用同步函数
如果不想等UI更新完毕就需要执行后面的代码, 那么使用异步函数
*/
dispatch_sync(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
NSLog(@"设置图片完毕 %@", image);
});
}
1.使用GCD延迟执行的好处:
2.示例如下:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(demo) userInfo:nil repeats:NO];
// [self performSelector:@selector(demo) withObject:nil afterDelay:3.0];
// 该方法中, 会根据传入的队列来决定回掉block在哪个线程中执行
// 如果传入的是主队列, 那么block会在主线程调用
// 如果传入的是全局队列, 那么block会在子线程中调用
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
NSLog(@"3秒之后执行 %@", [NSThread currentThread]);
});
}
- (void)demo
{
NSLog(@"%s", __func__);
}
#import "Person.h"
@implementation Person
- (NSArray *)books
{
/*
if (!_books) {
_books = @[
@"iOS开发",
@"Android开发",
@"高薪技巧"
];
}
*/
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_books = @[
@"iOS开发",
@"Android开发",
@"高薪技巧"
];
});
return _books;
}
@end
#import "Person.h"
@implementation ViewController
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
/*
// 注意: 千万不能把一次性代码当作懒加载来使用
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"我被执行了");
});
*/
Person *p1 = [Person new];
NSLog(@"%@", p1.books);
Person *p2 = [Person new];
NSLog(@"%@", p2.books);
}
@end
3执行结果:
--- (
"iOS\U5f00\U53d1",
"Android\U5f00\U53d1",
"\U9ad8\U85aa\U6280\U5de7"
)
--- (
"iOS\U5f00\U53d1",
"Android\U5f00\U53d1",
"\U9ad8\U85aa\U6280\U5de7"
)
(2)使用GCD一次性代码时:
--- (
"iOS\U5f00\U53d1",
"Android\U5f00\U53d1",
"\U9ad8\U85aa\U6280\U5de7"
)
--- (null)
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 1.定义变量记录原始文件夹和目标文件夹的路径
NSString *sourcePath = @"/Users/lisilong/Desktop/test";
NSString *destPath = @"/Users/lisilong/Desktop/longshao";
// 2.取出原始文件夹中所有的文件
NSFileManager *manager = [NSFileManager defaultManager];
NSArray *files = [manager subpathsAtPath:sourcePath];
// 开始转移文件时的时间
NSDate *beginDate = [NSDate date];
// 3.开始拷贝文件
for (NSString *fileName in files) {
// 3.1生产原始文件的绝对路径
NSString *sourceFilePath = [sourcePath stringByAppendingPathComponent:fileName];
// 3.2生产目标文件的绝对路径
NSString *destFilePath = [destPath stringByAppendingPathComponent:fileName];
// 3.3利用NSFileManager拷贝文件
[manager moveItemAtPath:sourceFilePath toPath:destFilePath error:nil];
}
// 文件转移结束时的时间
NSDate *endDate = [NSDate date];
// 转移文件用时:
NSLog(@"time=%f",[endDate timeIntervalSinceDate:beginDate]);
}
--- time=1.139403
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 1.定义变量记录原始文件夹和目标文件夹的路径
NSString *sourcePath = @"/Users/lisilong/Desktop/test";
NSString *destPath = @"/Users/lisilong/Desktop/longshao";
// 2.取出原始文件夹中所有的文件
NSFileManager *manager = [NSFileManager defaultManager];
NSArray *files = [manager subpathsAtPath:sourcePath];
// 开始转移文件时的时间
NSDate *beginDate = [NSDate date];
// 3.开始拷贝文件
/*
第一个参数: 需要遍历几次
第二个参数: 决定第三个参数的block在哪个线程中执行
第三个参数: 回掉
*/
dispatch_apply(files.count, dispatch_get_global_queue(0, 0), ^(size_t index) {
NSString *fileName = files[index];
// 3.1生产原始文件的绝对路径
NSString *sourceFilePath = [sourcePath stringByAppendingPathComponent:fileName];
// 3.2生产目标文件的绝对路径
NSString *destFilePath = [destPath stringByAppendingPathComponent:fileName];
// 3.3利用NSFileManager拷贝文件
[manager moveItemAtPath:sourceFilePath toPath:destFilePath error:nil];
});
// 文件转移结束时的时间
NSDate *endDate = [NSDate date];
// 转移文件用时:
NSLog(@"time=%f",[endDate timeIntervalSinceDate:beginDate]);
}
--- time=0.626504
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
dispatch_queue_t queue = dispatch_queue_create("com.longshao.lsl", DISPATCH_QUEUE_CONCURRENT);
__block UIImage *image1 = nil;
__block UIImage *image2 = nil;
// 1.开启一个新的线程下载第一张图片
dispatch_async(queue, ^{
NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/image/pic/item/77c6a7efce1b9d1632701663f5deb48f8c546479.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
image1 = image;
NSLog(@"图片1下载完毕");
});
// 2.开启一个新的线程下载第二张图片
dispatch_async(queue, ^{
NSURL *url = [NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/pic/item/18d8bc3eb13533fa0f2eb8c0acd3fd1f40345b47.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
image2 = image;
NSLog(@"图片2下载完毕");
});
// 3.开启一个新的线程, 合成图片
// 栅栏
dispatch_barrier_async(queue, ^{
// 1.开启图片上下文
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
// 2.将第一张图片画上去
[image1 drawInRect:CGRectMake(0, 0, 100, 200)];
// 3.将第二张图片画上去
[image2 drawInRect:CGRectMake(100, 0, 100, 200)];
// 4.从上下文中获取绘制好的图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// 5.关闭上下文
UIGraphicsEndImageContext();
// 4.回到主线程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = newImage;
});
NSLog(@"栅栏执行完毕了");
});
// 只要栅栏执行完毕后才会执行
dispatch_async(queue, ^{
NSLog(@"---------");
});
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
dispatch_queue_t queue = dispatch_queue_create("com.longshao.lsl", DISPATCH_QUEUE_CONCURRENT);
__block UIImage *image1 = nil;
__block UIImage *image2 = nil;
dispatch_group_t group = dispatch_group_create();
// 1.开启一个新的线程下载第一张图片
dispatch_group_async(group, queue, ^{
NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/image/pic/item/77c6a7efce1b9d1632701663f5deb48f8c546479.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
image1 = image;
NSLog(@"图片1下载完毕");
});
// 2.开启一个新的线程下载第二张图片
dispatch_group_async(group, queue, ^{
NSURL *url = [NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/pic/item/18d8bc3eb13533fa0f2eb8c0acd3fd1f40345b47.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
image2 = image;
NSLog(@"图片2下载完毕");
});
// 3.开启一个新的线程, 合成图片
// 只要将队列放到group中, 队列中的任务执行完毕, group就会发出一个通知
dispatch_group_notify(group, queue, ^{
NSLog(@"%@ %@", image1, image2);
// 1.开启图片上下文
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
// 2.将第一张图片画上去
[image1 drawInRect:CGRectMake(0, 0, 100, 200)];
// 3.将第二张图片画上去
[image2 drawInRect:CGRectMake(100, 0, 100, 200)];
// 4.从上下文中获取绘制好的图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// 5.关闭上下文
UIGraphicsEndImageContext();
// 4.回到主线程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = newImage;
});
});
}