Grand Central Dispatch,简称GCD,在异步执行任务的技术之一。

  一般将应用程序中记述的线程管理用的代码在系统级中实现,开发者只需要定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。这句话用代码表示如下:

dispatch_async(queue, ^{

/*

* 想要执行的任务

*/

});

  虽然多线程可以给开发贷来便利,在执行长时间的处理时仍可保证用户界面的响应性能。但是,如果过多使用多线程,就会消耗大量内存,引起大量的上下文切换,大幅度降低系统的响应性能。

  1、Dispatch Queue的种类:Serial Dispatch Queue和Concurrent Dispatch Queue

  (1)、Serial Dispatch Queue,简单的理解就是,使用一个线程,按照队列的规矩,任务排好队,一个接一个的顺序执行;

  (2)、Concurrent Dispatch Queue,简单的理解就是,XNU内核决定使用的线程数,并根据所需的线程执行处理,虽然也是排队,但是服务的窗口多了。

  2、dispatch_queue_create

  dispatch_queue_create函数的返回值为表示Dispatch Queue的“dispatch_queue_t类型”,创建如下:

  dispatch_queue_t queue = dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t  _Nullable attr#>);

  第一个参数:可以指定queue的名称,也可以设为NULL,命名推荐使用应用程序ID这种逆序全程域名(FQDN, fully qualified domin name),该名称在Xcode和Instruments的调试器中作为Disoatch Queue名称使用,另外,该名称也出现在应用程序崩溃时所生成的CrashLog中。如果嫌命名麻烦,在这里设的是NULL,调试的时候,会不会后悔没有为Dispatch Queue命名,这个就不好说了;

  第二个参数:指定创建Dispatch Queue的种类,可以指定为NULL,默认的是Serial Dispatch Queue,也可以设定为DISPATCH_QUEUE_CONCURRENT,这个生成的就是Concurrent Dispatch Queue。

  3、dispatch_release

  尽管有ARC这一通过编译器自动管理内存的优秀技术,但生成的Dispatch Queue必须由程序员负责释放。

  通过dispatch_queue_create函数生成的Dispatch Queue,在使用结束后通过dispatch_release函数释放,使用很简单:dispatch_release(<#object#>);

  看到release,由此可以推测出相应地也存在dispatch_retain函数,即Dispatch Queue也像OC的引用计数式管理内存一样,需要通过dispatch_retain函数和dispatch_release函数的引用计数来管理内存。

  拓展问题:一创建Dispatch Queue,然后在dispatch_async函数中追加Block到Dispatch Queue后,立即通过dispatch_release函数释放,是否可以?

  这样做完全没有问题,在dispatch_async函数中追加Block到Dispatch Queue,该Block就通过dispatch_retain函数持有了Dispatch Queue,这时即使立即释放Dispatch Queue,该Dispatch Queue由于被Block持有而不会被废弃,因而Block能够继续执行,Block执行结束后会释放Dispatch Queue,这时谁也不持有Dispatch Queue,这时Dispatch Queue才会被废弃。

  4、Main Dispatch Queue/Global Dispatch Queue

  实际上不用特意生成Dispatch Queue,系统也会给我们提供几个,那就是Main Dispatch Queue和Global Dispatch Queue。

  Main Dispatch Queue,可以理解为系统为我们开启的主队列,是在主线程中执行的Dispatch Queue,因为主线程就只有一个,所以Main Dispatch Queue是Serial Dispatch Queue。

  Main Dispatch Queue的获取方法如下:

  dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
  追加到Main Dispatch Queue的处理,是在主线程的RUNLoop中执行,所以在子线程处理任务时,会将用户界面的界面更新等一些必须在主线程中执行的处理追加到Main Dispatch Queue中。这与NSObject类的performSelectorOnMainThread这一方法相同。

  Global Dispatch Queue,可以理解为系统为我们开启的全局队列,是所有应用程序都能够使用的Concurrent Dispatch Queue。不用通过dispatch_queue_create函数生成Concurrent Dispatch Queue。只有获取Global Dispatch Queue就可以使用。

  Global Dispatch Queue的获取方法如下:

  dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(<#long identifier#>, <#unsigned long flags#>);

  第一个参数:Global Dispatch Queue的优先级,Global Dispatch Queue有四个执行优先级,分别是高优先级(High Priority)、默认优先级(Default Priority)、低优先级(Low Priority)、后台优先级(Background Priority)。Global Dispatch Queue的执行优先级作为线程的执行优先级使用,在向Global Dispatch Queue追加处理时,应选择与处理内容对应的执行优先级的Global Dispatch Queue。

  第二个参数:作为保留字段备用,一般为0。

  5、dispatch_after

  在指定时间后执行某个操作,可使用dispatch_after函数来实现。

  dispatch_after函数并不是在指定时间后执行处理,只是在指定时间追加处理到Dispatch Queue。虽然在有严格时间要求下使用会出现问题,但在大致时间执行处理,表现还是可以的。使用如下:

  dispatch_after(<#dispatch_time_t when#>, <#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>);

  第一个参数:指定时间用的dispatch_time_t类型的值,该值可用dispatch_time函数或dispatch_walltime函数获得;

  第二个参数:指定要追加处理的Dispatch Queue;

  第三个参数:指定要执行处理的Block;

  获取dispatch_time_t方法如下:
  dispatch_time_t time = dispatch_time(<#dispatch_time_t when#>, <#int64_t delta#>);

  第一个参数:指定的开始时间,这个值经常使用DISPATCT_TIME_NOW,这表示现在的时间。

  第二个参数:指定的延迟时间,如果是3秒后的话,这里应该填,3ull*NSEC_PER_SEC,“ull”是C语言的数值字面量,是显示表明类型时使用的字符串(表示"unsigned long long")。NSEC_PER_SEC这是以秒为单位,NSEC_PER_MSEC这是以毫秒为单位。

  6、Dispatch Group

  执行多个任务,全部结束后通知执行某一操作,这样就需要用到Dispatch Group。

  直接举个例子,一目了然。

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

dispatch_group_t group = dispatch_group_create();

// 任务放进 group

dispatch_group_async(group, queue, ^{

// 任务1

});

dispatch_group_async(group, queue, ^{

// 任务2

});

dispatch_group_async(group, queue, ^{

// 任务3

});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

// 任务全部完成后的处理

});

  三个任务都放进group里,都执行完后,调用dispatch_group_notify函数,dispatch_group_notify里面的queue,不管是什么样的queue,都没关系。

  任务结束后的处理,也可以使用dispatch_wait(<#object#>, <#timeout#>);第二个参数为等待的时间,将它设为DISPATCH_TIME_FOREVER,就意味永久等待,只要Dispatch Group的处理尚未结束,就会一直等待。注意:dispatch_group_wait,是会阻塞当前进程的,所以最好不要放在主线程执行,可以将它放到子线程中去。但从这点来看,推荐使用dispatch_group_notify。但是dispatch_group_wait还有一个用法,就是在指定时间后,任务是否执行完毕,返回值为0,就是任务全部完成,返回值不为0,就意味着Dispatch Group的某一个任务还在执行中,这个在某些场合还是有用的。

  7、dispatch_barrier_async

  在访问数据库或文件时,读取与读取操作是可以并行处理,但是读取和写入操作是相互冲突的,这个涉及到数据竞争问题,可以使用dispatch_barrier_async和Concurrent Dispatch Queue配合使用,解决这个问题。例子如下:

dispatch_queue_t queue = dispatch_queue_create("com.hehe.gcd.forBarrier", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{

//读操作

});

dispatch_async(queue, ^{

//读操作

});

dispatch_barrier_async(queue, ^{

//写操作

});

dispatch_async(queue, ^{

//读操作

});

dispatch_async(queue, ^{

//读操作

});

  为了更高效率的访问,读取操作追加到Concurrent Dispatch Queue中,写入操作放在dispatch_barrier_async里面,dispatch_barrier_async会等待它前面追加到Concurrent Dispatch Queue上的并行执行的处理全部结束之后,再执行dispatch_barrier_async中追加的处理,等这个写入操作完成后,才会将它之后的处理加入到Concurrent Dispatch Queue中,继续执行。

  8、dispatch_sync

  dispatch_async:async意味着“非同步”,就是讲指定的Block“非同步”的追加到指定的Dispatch Queue中,dispatch_async函数不做任何等待。简单的说就是,任务直接加到Dispatch Queue中,串行还是并行处理再看Dispatch Queue的设置。

  dispatch_sync:sync意味着“同步”,也就是讲指定的Block“同步”的追加到指定的Dispatch Queue中,dispatch_async函数会一直等待。简单的说就是,一旦调用dispatch_sync函数,处理结束之前,该函数都不会返回,其他的任务要等前一个任务执行完了,才会再加一个任务到Dispatch Queue中。

  dispatch_sync只能在当前线程下串行执行,而且,它不能放在主线程中,只要放在主线程中,就会引起死锁。

  9、dispatch_apply

  dispatch_apply函数是dispatch_sync和Dispatch Group的关联API,该函数将指定的Block追加到指定的Dispatch Queue中,并等待全部执行完成。

  dispatch_apply(<#size_t iterations#>, <#dispatch_queue_t  _Nonnull queue#>, <#^(size_t)block#>);

  第一个参数:重复次数。

  第二个参数:追加对象的Dispatch Queue。

  第三个参数:追加的处理,里面有index。

  由于dispatch_apply函数也与dispatch_sync相同,会等待处理执行结束,因此,推荐在dispatch_async函数中非同步的执行dispatch_apply函数,这样执行完了,在搞个dispatch_get_main_queue,就可以回主线程做一些更新操作。

  10、dispatch_suspend/dispatch_resume

  当追加大量处理到Dispatch Queue后,在处理的过程中,dispatch_suspend是挂起指定的Dispatch Queue,dispatch_resume是恢复指定的Dispatch Queue。

  dispatch_suspend(<#dispatch_object_t  _Nonnull object#>);

  dispatch_resume(<#dispatch_object_t  _Nonnull object#>);

  11、dispatch_once

  dispatch_once一般是在生成单例对象时使用。

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

<#code to be executed once#>

});

iOS开发——GCD总结的更多相关文章

  1. ios开发--GCD使用介绍:4-延迟执行操作

    在开发过程中,我们有时会希望把一些操作封装起来延迟一段时间后再执行.iOS开发中,有两种常用的方法可以实现延迟执行,一种是使用GCD,另外一种是使用NSRunLoop类中提供的方法. 1.使用GCD实 ...

  2. IOS开发GCD小结

    0. Brief Introduction GCD,全称Grand Central Dispath,是苹果开发的一种支持并行操作的机制.它的主要部件是一个FIFO队列和一个线程池,前者用来添加任务,后 ...

  3. iOS开发——GCD多线程详解

    GCD多线程详解 1. 什么是GCD Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,简单来说,GCD就是iOS一套解决多线程的机制,使用GCD能够最大限度简化多线程 ...

  4. IOS开发 GCD介绍: 基本概念和Dispatch Queue

    iOS的三种多线程技术 1.NSThread 每个NSThread对象对应一个线程,量级较轻(真正的多线程) 2.以下两点是苹果专门开发的“并发”技术,使得程序员可以不再去关心线程的具体使用问题 ØN ...

  5. iOS开发-GCD和后台处理

    一些生命周期函数的调用时间 打开应用时,调用 applicationWillEnterForeground: applicationDidBecomeActive: 按Home键,调用 applica ...

  6. iOS开发GCD的简单使用

    - (void)viewDidLoad { [super viewDidLoad]; // gcd 可以充分调用设备的 cpu 发挥最大性能,在 C 语言基础之上封装的 // dispatch_que ...

  7. iOS开发GCD(3)-数据安全

    /* 多个线程可能访问同一块资源,造成数据错乱和数据安全问题 为代码添加同步锁(互斥锁) */ -(void)synchronized{ @synchronized(self){ //需要锁住的代码, ...

  8. ios开发GCD(2)-dispatch_semaphore_t信号量计数器

    思考:现在有多个线程异步执行,我们想要同时最多只能执行2个或n个,该怎么办? dispatch_semaphore_t 看代码解析: NSLog(@"开始"); dispatch_ ...

  9. iOS开发之多线程技术——GCD篇

    本篇将从四个方面对iOS开发中GCD的使用进行详尽的讲解: 一.什么是GCD 二.我们为什么要用GCD技术 三.在实际开发中如何使用GCD更好的实现我们的需求 一.Synchronous & ...

随机推荐

  1. CorelDRAW设计制作超漂亮的3D立体字效果实例教程

    第一.打上招聘两个字,选择一个喜欢的字体,如下. 第二.把字体转曲,等待下一步编辑曲线做好准备,如下. 第三.选择形状工具,编辑字体形状,根据自己需要可任意编辑,如下. 第四.适当画一些装饰素材,比如 ...

  2. ZBrush软件中的笔触类型

    在ZBrush® 中我们通过各种笔触类型,确定在使用ZBrush®画笔进行绘制时画笔的变化方式及状态.使用多种画笔绘制根据选择不同的笔触组合绘制,能够得到繁多变化丰富的制作效果. 选择笔触的类型 点击 ...

  3. JS中的异步

    Hello,日常更新的我“浪”回来了!!! JS中有三座高山:异步和单线程.作用域和闭包.原型原型链 今天“浪”的主题是JS中的异步和单线程的问题. 主要从这三个方面入手 一.什么是异步(与同步作比较 ...

  4. [luogu1772 ZJOI2006] 物流运输 (最短路 线性dp)

    题目描述 物流公司要把一批货物从码头A运到码头B.由于货物量比较大,需要n天才能运完.货物运输过程中一般要转停好几个码头.物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪. ...

  5. os.getcwd()函数的用法

    获得当前路径 在Python中可以使用os.getcwd()函数获得当前的路径. 其原型如下所示: os.getcwd() 该函数不需要传递参数,它返回当前的目录.需要说明的是,当前目录并不是指脚本所 ...

  6. IIC 原理讲解

    IIC具体是什么这里我就不细说了,只收集一些关于IIC的原理. IIC总线优点是节约总线数,稳定,快速, 是目前芯片制造上非常 流行的一种总线,大多数单片机已经片内集成了IIC总线接口,无 需用户自己 ...

  7. linux 上安装 redis

    一.安装gcc Redis是c语言开发的. 安装 redis 需要 c 语言的编译环境.如果没有 gcc 需要在线安装. yum install gcc-c++ 二.下载 redis 链接:https ...

  8. NYIST 1070 诡异的电梯【Ⅰ】

    诡异的电梯[Ⅰ]时间限制:1000 ms | 内存限制:65535 KB难度:3 描述新的宿舍楼有 N(1≤N≤100000) 层 and M(1≤M≤100000)个学生. 在新的宿舍楼里, 为了节 ...

  9. 洛谷——P3258 [JLOI2014]松鼠的新家

    https://www.luogu.org/problem/show?pid=3258 题目描述 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到 ...

  10. QT5 OpenGL (六, 键盘事件, 开关灯,放大缩小综合运用)

    概要 实例效果图 立体图放大图 立体图缩小图 不加矢量开灯图 不加矢量关灯图 加矢量关灯图1 加矢量关灯图2 部分代码展示 主要内容解析 QT键盘事件 立体图形的放大和缩小 上下左右键以及A键D争键控 ...