GCD 必修技能
本文来自《iOS程序员面试笔试宝典》,纯手工打造 只为学习用,加深印象
GCD必修技能
GCD ,又叫大中央调度,它对线程操作进行了封装,加入了很多新的特性,内部进行了效率优化,提供了简洁的C语言接口,使用更加简单高效、也是苹果公司推荐的方式
必修内容
1、同步dispatch _sync与异步dispatch _async任务派发。
2、串行队列与并发队列dispatch _queue _t。
3、dispatch _once _t只执行一次。
4、dispatch _after 延后执行。
5、dispatch _group _t组调度。
串行与并发
这个概念在创建操作队列的时候有宏定义参数,用来指定创建的是串行队列还是并行队列。
串行指队列内任务一个接一个的执行,任务之间要依次等待不可重合,且添加的任务按照先进先出的顺序执行、但并不是指这就是单线程,只是同一个串行队列内的任务需要依次等待排队执行避免出现竞态条件,仍然可以创建多个串行队列并行的执行任务。也就是说,串行队列内是串行的,串行队列之间扔然是可以并行的,同一个串行队列内的任务的执行顺序是确定的,且可以创建任意多个串行队列。
并行指同一个队列先后添加的多个任务可以同时并列执行,任务之间不会相互等待,且这些任务的执行顺序和执行过程不可预测
同步和异步任务派发
GCD多线程编程时经常会使用 dispatch _async 和 dispatch _sync 函数向指定队列中添加任务块,区别就是同步和异步。
同步指阻塞当前线程,即要等添加的耗时任务块 block 完成后, 函数才能返回,后面的代码才可以继续执行。如果在主线程上,那么会发生阻塞,用户会感觉应用不响应,这是要避免的。有时需要使用同步任务的原因是想保证先后添加的任务要按照编写的逻辑顺序依次执行。
异步指将任务添加到队列后函数立刻返回,后面的代码不用等待添加的任务完成返回即可继续执行。异步提交无法确认任务的执行顺序。
通过代码可以比较异步和同步任务的区别。
// 1、提交异步任务
NSLog(@"开始提交异步任务:");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//耗时任务
[NSThread sleepForTimeInterval:10];
});
NSLog(@"异步任务提交成功!");
// 提交同步任务
NSLog(@"开始提交同步任务:");
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//耗时任务
[NSThread sleepForTimeInterval:10];
});
NSLog(@"同步任务提交成功!");
打印结果:
2019-12-05 13:56:40.011189+0800 GCD必备技能[45418:2673665] 开始提交异步任务:
2019-12-05 13:56:40.011465+0800 GCD必备技能[45418:2673665] 异步任务提交成功!
2019-12-05 13:56:40.011621+0800 GCD必备技能[45418:2673665] 开始提交同步任务:
2019-12-05 13:56:50.012151+0800 GCD必备技能[45418:2673665] 同步任务提交成功!
查看结果,异步任务提交后自己就执行下一步打印提交成功,不会阻碍当前线程,提交的任务会在后台去执行;而提交同步任务要等到提交的后台任务结束后才会继续执行当钱线程的下一步。此处在主线程上添加同步任务就会阻塞主线程,导致后面的显示要延迟,影响用户体验。
dispatch _queue _t
GCD队列有两种类型:并发队列和串行队列。他们的区别上面已经提到,具体创建的方法很简单,要提供两个参数,一个是标记该自定义队列的唯一字符串;另一个是指定串行队列还是并发队列的宏参数。
//创建一个并发队列
dispatch_queue_t concurrent_queue = dispatch_queue_create("demo.gcd.concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
//创建一个串行队列
dispatch_queue_t serial_queue = dispatch_queue_create("demo.gcd.serial_queue", DISPATCH_QUEUE_SERIAL);
另外,GCD还提供了几个常用的全局队列及主队列,体重全局队列本质是并发队列,主队列本质是串行队列,获取方法如下:
//获取主队列(在主线程执行)
dispatch_queue_t main_queue = dispatch_get_main_queue();
//获取不同优先级的全局队列(优先级从高到低);
dispatch_queue_t queue_high = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_t queue_defaut =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t queue_low = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t queue_background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch _once _t
这个函数控制指定代码只会被执行一次,常用来实现单例模式。这里以单例模式实现模块代码为例展示 dispatch _once _t的用法,其中的实例化语句只会被执行一次。
+(instancetype)sharedInstance
{
static dispatch_once_t once = 0;
static id sharedInstance = nil;
dispatch_once(&once, ^{
//只实例化一次
sharedInstance = [[self alloc]init];
});
return sharedInstance;
}
dispatch _after
通过该函数可以让要提交的任务在从提交开始后的指定时间执行,也就是定时延迟执行提交的任务,使用方法和简单。
//定义延时时间
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0*NSEC_PER_SEC));
dispatch_after( delay, dispatch_get_main_queue(), ^{
// 要执行的任务
});
dispatch _group _t
组调度可以实现等待一组操作都完成后执行后续操作。典型的例子是大图片的下载,例如可以将大图片分成几块同时下载,等各部分都下载完成后在继续讲图片拼接起来 提高下载效率。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"3");
});
dispatch_group_async(group, queue, ^{
NSLog(@"4");
});
dispatch_group_async(group, queue, ^{
NSLog(@"5");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"最终。。。。");
})
同步代码到主线程
对于UI的更新代码,必须要在主线程上执行才会及时有效,当当前代码不在主线程时,需要将UI更新的部分代码单独同步到主线程。同步的方法有3种,可以使用 NSThread类的 performSelectorOnMainThread 方法或者 NSOperationQueue类的mainQueue主队列来进行同步,但推荐直接使用GCD方法
//回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
});