有了GCD,我们iOS开发人员确实省事了不少,不用再去对线程有太多的关注。以前拿的是白面的钱,操着白粉的心,就怕几个字眼:死锁,同步,不可控等等。但是现在是拿着白面的钱,操着白面的心,小爷好多事不管了。好了不多说 ,进入主题。
一、概念
Grand ( [ɡrænd],豪华的) Central (['sɛntrəl],中枢的) Dispatch ([dɪ'spætʃ],分派) 的缩写:豪华的(牛逼的中枢分派器)。
是纯C语言写的,不过不要担心指针的残害。虽然是C写的,但是用起来非常方便和容易,用用就知道。
二、GCD的优势
1. GCD是苹果公司为多核的并行运算提出的解决方案
2. GCD会自动利用更多的CPU内核(比如双核、四核)
3. GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
4. 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
三、GCD的核心概念
1. 任务:执行什么操作
2. 队列:用来存放任务
四、GCD的使用步骤
它的使用就分办三个步骤,一定要记住,记住就任你操作,任你装。记不住就改行吧,我差点儿改行了,结果又记住了(多用就好)
1. 定制任务,确定想做的事情
2. 创建队列(获取队列)
3. 将任务添加到队列中。
GCD会自动将队列中的任务取出,放到对应的线程中执行;
任务的取出遵循队列的FIFO原则:先进先出,后进后出。
注意:
程序员要做的是将任务添加到队列就好。然后队列按照程序员指定的方式,调度任务执行。执行方式是我们(小猿来指定的),有同步 和 异步 两种执行方式
同步:一个任务没有结束,就不会执行下一个任务
异步:不用等待任务执行完毕,就会执行下一任务
说到底同步 和 异步 有什么区别呢?
1> 同步:只能在当前线程中执行任务,不具备开启新线程的能力
2> 异步:可以在新的线程中执行任务,具备开启新线程的能力,注意是具备开启新线程能力,不是 “一 定”
刚说不练就是耍流氓,我们来实操一下:
上面说了,GCD的使用,主要为两步,还记得吧?
第一、定制任务(其实就是一个block):
void(^task)() = ^{
NSLog(@"%@",[NSThread currentThread]);
};
第二、创建队列:
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
第三、将任务添加到队列中,并且会执行
dispatch_sync(q, task);
完美结束,简单吗?简单吗?
只不过我们经常把上面的步骤合并到一起,就是下面的效果
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@",[NSThread currentThread]);
});
五、队列分类
上面说过了,会务是要放在队列去执行。那么队列有哪几类呢?有两类。
1. 串行队列(SerialDispatch Queue)
让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
2. 并行队列(ConcurrentDispatch Queue)
并行队列可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并且,并且,并且 ,并发功能只有在异步(dispatch_async)函数下 异步,异步 函数下才有效。为什么呢?大家想想。
说到这里我觉得有必要强调几个术语:同步,异步,并行,串行。大家有没有懵的?有没有概念混淆的?
1. 同步和异步主要影响:能不能开启新的线程
同步:只是在当前线程中执行任务,不具备开启新线程的能力
异步:可以在新的线程中执行任务,具备开启新线程的能力,是具备开启,不是一定开启
2. 并发和串行主要影响:任务的执行方式
并发:允许多个任务并发(同时)执行
六、创建队列
使用 dispatch_queue_create 函数创建队列
1. 创建并行队列:
A. dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
参数1:队列名称
参数2:队列的类型:DISPATCH_QUEUE_CONCURRENT表示并行,DISPATCH_QUEUE_SERIAL表示串行,或者这个参数写NULL也表示串行
B. GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建
使用dispatch_get_global_queue函数获得全局的并发队列
dispatch_queue_t dispatch_get_global_queue(
dispatch_queue_priority_t priority,// 队列的优先级
unsigned long flags);// 此参数暂时无用,是未来可能会用到的,目前给0即可
获得全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
其实参数1严格上来说涉及到系统的适配:在iOS8及上版本称作:服务质量,iOS7称作:高度优先有级。不同系统传不同参数。如下
iOS 8 服务质量
QOS_CLASS_USER_INTERACTIVE 用户交互(希望线程快速被执行,不要用好使的操作)
QOS_CLASS_USER_INITIATED 用户需要的(同样不要使用耗时操作)
QOS_CLASS_DEFAULT 默认的(给系统来重置队列的)
QOS_CLASS_UTILITY 使用工具(用来做耗时操作)
QOS_CLASS_BACKGROUND 后台
QOS_CLASS_UNSPECIFIED 没有指定优先级
iOS 7 调度的优先级
- DISPATCH_QUEUE_PRIORITY_HIGH 2 高优先级
- DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认优先级
- DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
- DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级
由于 默认模式 在两种模式的宏定义中都是0,所以我们一般都传了0,可以直接适配系统版本。
但是在注意,注意,注意;
不管什么系统版本都不要选择 后台 模式 ,不然此线程会调用特别慢,慢到您想放弃iOS,直接转行捡垃圾去。不信你试试。
2. 创建串行队列
GCD中获得串行有2种途径:
A. 使用 dispatch_queue_create 函数创建串行队列
创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)
dispatch_queue_t queue = dispatch_queue_create("com.xxx.xxx", NULL);
B. 使用主队列(跟主线程相关联的队列)
主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行
使用 dispatch_get_main_queue() 获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
七、一副图看清所有GCD关系:
注:图片来源于”潭州课堂“,一个让你受益匪浅的课堂,大家可以去学习下,其实不止图片,这些内容也是我对课程的一个总结。
最后再来补充两点:
1,全局队列 & 并发队列
大家知道,全局队列本质也是并发队列,那我们在项目中要使用并发队列时候,具体用哪一个哪?
个人认为,当业务比较复杂时,最好使用手动创建并发队列,原因是,如果内部出现问题我们可以快速定位到是哪个队
列(创建时有队列名称)的任务出了问题,如果下图:
我想大家见过这样的崩溃吧?不是崩在具体某一行代码上,面是上面这样的情况。虽然我们根据 1 一眼能够看出来是 数组越界 导致的,但是如果项目比较大,你知道是哪个数组吗?知道吗?知道吗?。但是,我们如果使用了手动创建的队列,那么可以根据 2 得到,是“xiaoyuancai”这个队列的任务出了问题,从而快速定位,484(是不是) 484 ?
当然也不是说全局队列没用,当我们业务简单,那完全可以使用全局队列。
2,并发队列 & 串行队列
并发队列的优势:并发,能够调度多个线程,执行效率高。但是对于用于而言会觉得太 费电 ,手机太 烫
串行队列的优势:那当然省电了。不过缺点也明显:串行队列:一个一个执行,执行效率低。
差不多了,至于GCD其它操作再起一篇来介绍。大家准备好来吐槽!!!