一、前言
Grand Central Dispatch(GCD)作为iOS和MacOS开发中不可或缺的工具,它的重要性不言而喻。作为一个喜欢刨根问底的猿,知其然更要知其所以然。网上关于GCD解析的文章不多,仅有的几篇使用的源码版本很老,libdispatch
从187.10发展到913.60,其内部已经发生了很多变化。趁工作之余断断续续把GCD的源码libdispatch-913.60.2.tar.gz看了一下,算是对它的底层原理有了一个大致的了解。
本篇作为系列文章的第一篇,先讲一下GCD中“对象”和数据结构。
为什么“对象”加引号
libdispatch
库是纯C语言编写的,并不存在类似面向对象语言中的类、对象之类的概念,它的所有对象(如:dispatch_object_s
, dispatch_queue_s
, dispatch_semaphore_s
等)都是定义为struct(dispatch_object_t
是union类型)。
为了实现类似面向对象中的“继承”效果,GCD使用了若干个DISPATCH_XXX_HEADER
宏,将“基类”的内容在了“子类”结构体内存布局的起始处重写了一次,实现了类似“继承”的概念。注意,这里的继承与OOP中的继承不一样,看不到诸如extends/:之类的继承符号的。
二、结构分析
libdispatch中定义了很多结构体,这里只选取比较常用的几个,不想看过程的同学可以下面的代码分析。
先上图,GCD中的继承结构:
2.1 dispatch_object_t
从上图可以看到,dispatch_object_t
可以看做GCD中所有“类”的“基类”,看下其代码结构
1 | typedef union { |
dispatch_object_t
被定义为union,熟悉C语言的同学应该知道:union可以表示任意它里面定义的数据类型,union的大小为这些数据类型中最大的数据类型的大小。这里利用了union的特性将dispatch_object_t
定义为所有子类:dispatch_xxx_s
的基类。
2.2 _os_object_s
1 | typedef struct _os_object_s { |
- isa指针,应该类似于OC对象中的isa指针,用于判断当前dispatch object的类型,从下面的代码中可以看出一二:
1 | void *_dispatch_object_alloc(const void *vtable, size_t size) |
- os_obj_ref_cnt/os_obj_xref_cnt,从命名方式猜测,应该是引用计数,与GCD的内存管理相关。从代码中的备注发现,二者分别为object在GCD内部和外部使用的引用计数。
2.3 dispatch_object_s
1 | struct dispatch_object_s { |
上述为宏替换后的代码,原始代码中dispatch_object_s
只有_DISPATCH_OBJECT_HEADER(object)
宏
2.4 dispatch_queue_s
1 | struct dispatch_queue_s { |
2.5 dispatch_continuation_s
1 | typedef struct dispatch_continuation_s { |
从结构体中的dispatch_function_t
类型,可以猜测dispatch_continuation_s
与GCD的任务有关,我们向GCD中提交的任务,无论是Block或者函数形式,最终都转化成dispatch_continuation_s
2.6 dispatch_group_s
1 | struct dispatch_group_s { |
与其他结构体内容大同小异,需要关注的是这两个参数:
dg_notify_head
dg_notify_tail
。
我们知道dispatch_group_notify
可以用来做线程同步,那么猜测猜测这两个变量与实现dispatch_group_notify
功能相关,当group中内容执行完毕后,通知dg_notify
链表中的任务,_head
和_tail
用于定位这些需要接收通知的任务的位置。
在semaphore.c中找到相关源码:
1 | static inline void |
2.7 dispatch_source_s
1 | struct dispatch_source_s { |
dispatch_source_s
继承自dispatch_queue_s
,从字面意思可称为调度源,它的作用是当有一些特定的较底层的系统事件发生时,调度源会捕捉到这些事件,然后可以做其他的逻辑处理,调度源有多种类型,分别监听对应类型的系统事件。GCD提供了六大类调度源:
- Timer Dispatch Source:定时调度源。
- Signal Dispatch Source:监听UNIX信号调度源,比如监听代表挂起指令的SIGSTOP信号。
- Descriptor Dispatch Source:监听文件相关操作和Socket相关操作的调度源。
- Process Dispatch Source:监听进程相关状态的调度源。
- Mach port Dispatch Source:监听Mach相关事件的调度源。
- Custom Dispatch Source:监听自定义事件的调度源。
可以监听12个类型的事件(可以在source.h文件中查看):
- DISPATCH_SOURCE_TYPE_DATA_ADD,自定义事件,可以通过dispatch_source_get_data函数获取事件变量数据,在我们自定义的方法中可以调用dispatch_source_merge_data函数向调度源设置数据
- DISPATCH_SOURCE_TYPE_DATA_OR,同上
- DISPATCH_SOURCE_TYPE_DATA_REPLACE,同上
- DISPATCH_SOURCE_TYPE_MACH_SEND,Mach内核端口发送事件
- DISPATCH_SOURCE_TYPE_MACH_RECV,Mach内核端口监听事件
- DISPATCH_SOURCE_TYPE_MEMORYPRESSURE,内存压力事件,分为三个等级:NORMAL、WARN、CRITICAL
- DISPATCH_SOURCE_TYPE_PROC,进程相关事件,如进程退出、创建子线程、收到UNIX信号等事件
- DISPATCH_SOURCE_TYPE_READ,IO读操作事件,如文件或socket的读操作
- DISPATCH_SOURCE_TYPE_WRITE,IO写操作事件
- DISPATCH_SOURCE_TYPE_SIGNAL,进程接收Unix内核信号事件
- DISPATCH_SOURCE_TYPE_TIMER,定时器事件
- DISPATCH_SOURCE_TYPE_VNODE,文件状态改变事件,如文件移动、删除、重命名等