本文是作者在学习iOS底层代码时所著,文章中会尽量使用确定性的词汇,旨在帮助想要轻度深入的读者能够快速了解iOS底层实现。文章中内容的逻辑自洽,但内容的正确性需要读者自行甄别,仅供参考。
上篇文章中介绍了DispatchQueue中的基础用法,但也产生了一些疑问,在这篇文章中,我们先来看看,队列是如何创建的。
数据结构
我们平时所说的队列就是dispatch_queue_s,根据dispatch_queue_s的源码,再结合这个结构体在源码中的具体使用,列出了它的几个比较重要的属性:
struct dispatch_queue_s {
// 熟悉一下这些属性
unsigned long dq_serialnum; // 队列的序号
const char *dq_label; // 标签
dispatch_priority_t dq_priority; // 优先级
struct dispatch_queue_s *volatile do_next;
struct dispatch_queue_s *do_targetq; // target
void *do_ctxt;
struct dispatch_object_s *volatile dq_items_tail
const struct dispatch_queue_vtable_s *do_vtable; // 虚拟表
do_ref_cnt; // 引用计数
do_xref_cnt; // 外部引用计数
uint64_t volatile dq_state,
dispatch_lock dq_state_lock,
uint32_t dq_state_bits
接下来,我们看看dispatch_queue_s是如何创建的。
关于
dispatch_queue_s和dispatch_queue_t理解为相同即可
队列创建
在swift中我们是这样创建一个队列的
let queue = DispatchQueue(label: "com.concurrent", attributes: .concurrent)
相对应的源码为dispatch_queue_create,
// 这里dispatch_queue_t直接理解为dispatch_queue_s即可
dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
{
return _dispatch_lane_create_with_target(label, attr,
DISPATCH_TARGET_QUEUE_DEFAULT, true);
}
其中传入的label和attr就如字面意思一样,接着看_dispatch_lane_create_with_target,
DISPATCH_NOINLINE
static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
dispatch_queue_t tq, bool legacy)
{
// 一开始我们获取到一个dqai,它包含了一些创建队列时需要的参数,后面会详细分析获取的具体实现
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
// 这里官方的注释已经告诉了我们,第一步是标准化参数,这些参数就是指qos,overcommit和ta,
// 我们就以这个为线索接着往下看
//
// Step 1: Normalize arguments (qos, overcommit, tq)
//
// 标准化qos
dispatch_qos_t qos = dqai.dqai_qos;
// 在!HAVE_PTHREAD_WORKQUEUE_QOS这种条件下,DISPATCH_QOS_USER_INTERACTIVE和DISPATCH_QOS_MAINTENANCE两种状态不存在,
// 所以分别调整为DISPATCH_QOS_USER_INITIATED和DISPATCH_QOS_BACKGROUND。
#if !HAVE_PTHREAD_WORKQUEUE_QOS
if (qos == DISPATCH_QOS_USER_INTERACTIVE) {
dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;
}
if (qos == DISPATCH_QOS_MAINTENANCE) {
dqai.dqai_qos = qos = DISPATCH_QOS_BACKGROUND;
}
#endif // !HAVE_PTHREAD_WORKQUEUE_QOS
// 标准化overcommit
_dispatch_queue_attr_overcommit_t overcommit = dqai.dqai_overcommit;
// 在使用diapatch_create_queue()创建队列时,传入的tq是DISPATCH_TARGET_QUEUE_DEFAULT这个宏,而这个宏对应的值为NULL
// 所以在这种情况下if的条件表达式总为false,不会执行
if (overcommit != _dispatch_queue_attr_overcommit_unspecified && tq) {
if (tq->do_targetq) {
DISPATCH_CLIENT_CRASH(tq, "Cannot specify both overcommit and "
"a non-global target queue");
}
}
// 同理,这里也不会执行
if (tq && dx_type(tq) == DISPATCH_QUEUE_GLOBAL_ROOT_TYPE) {
// Handle discrepancies between attr and target queue, attributes win
if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
if (tq->dq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) {
overcommit = _dispatch_queue_attr_overcommit_enabled;
} else {
overcommit = _dispatch_queue_attr_overcommit_disabled;
}
}
if (qos == DISPATCH_QOS_UNSPECIFIED) {
qos = _dispatch_priority_qos(tq->dq_priority);
}
tq = NULL;
}
// 同理,这里也不会执行
else if (tq && !tq->do_targetq) {
// target is a pthread or runloop root queue, setting QoS or overcommit
// is disallowed
if (overcommit != _dispatch_queue_attr_overcommit_unspecified) {
DISPATCH_CLIENT_CRASH(tq, "Cannot specify an overcommit attribute "
"and use this kind of target queue");
}
}
// 最终会执行这里的代码
else {
// 这里我们直接假定overcommit等于_dispatch_queue_attr_overcommit_unspecified,
if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
// Serial queues default to overcommit!
// 串行队列启动overcommit,并行队列关闭overcommit
overcommit = dqai.dqai_concurrent ?
_dispatch_queue_attr_overcommit_disabled :
_dispatch_queue_attr_overcommit_enabled;
}
}
// 标准化tq
// tq为NULL,所以会走if表达式进行赋值
if (!tq) {
// _dispatch_get_root_queue这个函数的内容很多,这里我们先从字面意思去理解,这个函数是获取root队列,
// 并且这个root队列是根据我们传入了qos和overcommit而获得的,我们先不去管内部发生了什么,接着往下看
tq = _dispatch_get_root_queue(
qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos,
overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq;
if (unlikely(!tq)) {
DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
}
}
// 从官方的注释中能够了解到,第一步已经结束了,其中的qos, overcommit, tq都已经标准化(被合理地赋值了),
// 现在开始第二步,初始化队列
//
// Step 2: Initialize the queue
//
if (legacy) {
// if any of these attributes is specified, use non legacy classes
// 这里dqai_inactive为0,dqai_autorelease_frequency为DISPATCH_AUTORELEASE_FREQUENCY_INHERIT,也为0,按理说表达式不成立,
// 但多篇文章中都说了这样一句话"默认是会给dqa_autorelease_frequency指定为DISPATCH_AUTORELEASE_FREQUENCY_INHERIT,所以这个判断式是成立的",
// 可能以前的代码中DISPATCH_AUTORELEASE_FREQUENCY_INHERIT值大于0吧,我们按照表达式不成立来理解,
// 接下来的代码不会执行,legacy依然为true
if (dqai.dqai_inactive || dqai.dqai_autorelease_frequency) {
legacy = false;
}
}
// 这个参数很重要,最终是由它来执行任务
const void *vtable;
// 由于legacy为true,dqf会被赋值为DQF_MUTABLE,即便我们分析错了,目前来说问题也不大,因为我们后面不会看dqf的值,这里也只是做一个了解
dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
if (dqai.dqai_concurrent) {
// vtable被赋值为OS_dispatch_queue_concurrent_class
vtable = DISPATCH_VTABLE(queue_concurrent);
} else {
// vtable被赋值为OS_dispatch_queue_serial_class
vtable = DISPATCH_VTABLE(queue_serial);
}
// dqai_autorelease_frequency为DISPATCH_AUTORELEASE_FREQUENCY_INHERIT,不会走任何一个case
switch (dqai.dqai_autorelease_frequency) {
case DISPATCH_AUTORELEASE_FREQUENCY_NEVER:
dqf |= DQF_AUTORELEASE_NEVER;
break;
case DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM:
dqf |= DQF_AUTORELEASE_ALWAYS;
break;
}
if (label) {
const char *tmp = _dispatch_strdup_if_mutable(label);
// strdup是copy的意思,表面上看tmp跟label会相等吧
// 就认为表达式为false,以下代码不执行
if (tmp != label) {
dqf |= DQF_LABEL_NEEDS_FREE;
label = tmp;
}
}
// 为dq分配内存空间,让dq持有vtable
dispatch_lane_t dq = _dispatch_object_alloc(vtable,
sizeof(struct dispatch_lane_s));
// 根据dqf,是否为串行/并行队列,是否激活来初始化dq
_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));
// 继续初始化dq
dq->dq_label = label;
dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos,
dqai.dqai_relpri);
if (overcommit == _dispatch_queue_attr_overcommit_enabled) {
// overcommit的信息存储在dq_priority中
dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
}
if (!dqai.dqai_inactive) {
// 从tq中继承优先级配置
_dispatch_queue_priority_inherit_from_target(dq, tq);
// 从tq中继承wlh信息
_dispatch_lane_inherit_wlh_from_target(dq, tq);
}
_dispatch_retain(tq);
// 依赖tq,最终执行任务的队列是tq
dq->do_targetq = tq;
_dispatch_object_debug(dq, "%s", __func__);
return _dispatch_trace_queue_create(dq)._dq;
}
从源码的注释中,我们可以大致了解到队列(dispatch_queue_t)创建的过程,其中有两点非常重要,
- 从root队列中获取到一个队列,然后设置该队列为target,这意味着我们创建的队列只是一个代理,真正执行任务的是获取到的root队列;
- 根据队列类型(串行、并行)初始化队列(dispatch_queue_t)中的vtable,vtable存放了队列执行任务的各种逻辑;
接下来我们一起来详细了解下,它们的具体实现,
root queue
static inline dispatch_queue_global_t
_dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
{
if (unlikely(qos < DISPATCH_QOS_MIN || qos > DISPATCH_QOS_MAX)) {
DISPATCH_CLIENT_CRASH(qos, "Corrupted priority");
}
// qos的值为1...6, overcommit为0或1
// 2 * (qos - 1) + overcommit的值为0...11一共12个队列
// 其中双数0, 2, 4, 6, 8, 10为普通队列,单数1, 3, 5, 7, 9, 11为过载(overcommit)队列
return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}
接着看_dispatch_root_queues,
struct dispatch_queue_global_s _dispatch_root_queues[] = {
#define _DISPATCH_ROOT_QUEUE_IDX(n, flags) \
((flags & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) ? \
DISPATCH_ROOT_QUEUE_IDX_##n##_QOS_OVERCOMMIT : \
DISPATCH_ROOT_QUEUE_IDX_##n##_QOS)
#define _DISPATCH_ROOT_QUEUE_ENTRY(n, flags, ...) \
[_DISPATCH_ROOT_QUEUE_IDX(n, flags)] = { \
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global), \
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE, \
.do_ctxt = _dispatch_root_queue_ctxt(_DISPATCH_ROOT_QUEUE_IDX(n, flags)), \
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL), \
.dq_priority = flags | ((flags & DISPATCH_PRIORITY_FLAG_FALLBACK) ? \
_dispatch_priority_make_fallback(DISPATCH_QOS_##n) : \
_dispatch_priority_make(DISPATCH_QOS_##n, 0)), \
__VA_ARGS__ \
}
_DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, 0,
.dq_label = "com.apple.root.maintenance-qos",
.dq_serialnum = 4,
),
_DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.maintenance-qos.overcommit",
.dq_serialnum = 5,
),
_DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, 0,
.dq_label = "com.apple.root.background-qos",
.dq_serialnum = 6,
),
_DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.background-qos.overcommit",
.dq_serialnum = 7,
),
_DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, 0,
.dq_label = "com.apple.root.utility-qos",
.dq_serialnum = 8,
),
_DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.utility-qos.overcommit",
.dq_serialnum = 9,
),
_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
.dq_label = "com.apple.root.default-qos",
.dq_serialnum = 10,
),
_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,
DISPATCH_PRIORITY_FLAG_FALLBACK | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.default-qos.overcommit",
.dq_serialnum = 11,
),
_DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, 0,
.dq_label = "com.apple.root.user-initiated-qos",
.dq_serialnum = 12,
),
_DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.user-initiated-qos.overcommit",
.dq_serialnum = 13,
),
_DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, 0,
.dq_label = "com.apple.root.user-interactive-qos",
.dq_serialnum = 14,
),
_DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.user-interactive-qos.overcommit",
.dq_serialnum = 15,
),
};
看到这里,获取root队列的源码就结束了,可能依然想问,这个有什么用呢?我们直接举个例子来解释root队列相关的内容,先回到我们最初队列创建的swift代码,
// 为了更好理解,我们明确地给qos赋值为.default
let queue = DispatchQueue(label: "com.concurrent", qos: .default, attributes: .concurrent)
然后我们直接使用qos来获取root队列,
// 根据定义,我们获取到qos为4,
#define DISPATCH_QOS_DEFAULT ((dispatch_qos_t)4)
// 由于是并行队列overcommit为0,
// 2 * (qos - 1) + overcommit的计算结果为6
_dispatch_root_queues[2 * (qos - 1) + overcommit];
// 对应的结果为
_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
.dq_label = "com.apple.root.default-qos",
.dq_serialnum = 10,
)
最后的结论,当我们在qos为.default的多个自定义并行队列中执行任务时,任务都是交到了名称为com.apple.root.default-qos的root队列中来。
vtable
源码中,我们提到的重要参数vtable,在创建自定义并行队列时,是这样,在dispatch_async的解析中,会进行详细地分析,
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_concurrent, lane,
.do_type = DISPATCH_QUEUE_CONCURRENT_TYPE,
.do_dispose = _dispatch_lane_dispose,
.do_debug = _dispatch_queue_debug,
.do_invoke = _dispatch_lane_invoke,
.dq_activate = _dispatch_lane_activate,
.dq_wakeup = _dispatch_lane_wakeup,
.dq_push = _dispatch_lane_concurrent_push,
);
_dispatch_queue_attr_to_info
除了刚刚提到的两个非常重要的内容,我们可以继续看看,在队列(dispatch_queue_t)创建的过程中,我们传入的attributes参数是如何工作的,
dispatch_queue_attr_info_t
_dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa)
{
dispatch_queue_attr_info_t dqai = { };
// 串行队列时dqa为nil,会返回只包含默认值的dqai,也就是其属性全是0
if (!dqa) return dqai;
#if DISPATCH_VARIANT_STATIC
if (dqa == &_dispatch_queue_attr_concurrent) {
dqai.dqai_concurrent = true;
return dqai;
}
#endif
// 判断dqa和_dispatch_queue_attrs的大小关系,
if (dqa < _dispatch_queue_attrs ||
dqa >= &_dispatch_queue_attrs[DISPATCH_QUEUE_ATTR_COUNT]) {
#ifndef __APPLE__
if (memcmp(dqa, &_dispatch_queue_attrs[0],
sizeof(struct dispatch_queue_attr_s)) == 0) {
dqa = (dispatch_queue_attr_t)&_dispatch_queue_attrs[0];
} else
#endif // __APPLE__
// 如果越界,则报错
DISPATCH_CLIENT_CRASH(dqa->do_vtable, "Invalid queue attribute");
}
// 这里可以看出,前面的判断是为了保证计算出的idx处于合理范围
size_t idx = (size_t)(dqa - _dispatch_queue_attrs);
// 根据idx生成具体的值
// 这里的计算能比较容易看出,是在按位取值,
// 根据dqa提供的数据初始化dqai,
// 不难看出,只要知道idx的值,下面的值都能够通过计算获得,但我没看懂两个结构体相减应该是多少
// 不过,后面我会按照我的理解,推测出其中的一些值
dqai.dqai_inactive = (idx % DISPATCH_QUEUE_ATTR_INACTIVE_COUNT);
idx /= DISPATCH_QUEUE_ATTR_INACTIVE_COUNT;
dqai.dqai_concurrent = !(idx % DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT);
idx /= DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT;
dqai.dqai_relpri = -(int)(idx % DISPATCH_QUEUE_ATTR_PRIO_COUNT);
idx /= DISPATCH_QUEUE_ATTR_PRIO_COUNT;
dqai.dqai_qos = idx % DISPATCH_QUEUE_ATTR_QOS_COUNT;
idx /= DISPATCH_QUEUE_ATTR_QOS_COUNT;
dqai.dqai_autorelease_frequency =
idx % DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;
idx /= DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;
dqai.dqai_overcommit = idx % DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;
idx /= DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;
return dqai;
}
为了能够知道dqai的属性的初始化值,我们需要看一些swift的源码,
// 这是DispatchQueue的初始化方法,我们假设传入的参数都是默认值,
public convenience init(
label: String,
qos: DispatchQoS = .unspecified,
attributes: Attributes = [],
autoreleaseFrequency: AutoreleaseFrequency = .inherit,
target: DispatchQueue? = nil)
{
// 这里获取的attr,就是之前的dqa,并且后面的代码不会更改attr
var attr = attributes._attr()
// 这里为false,不会执行
if autoreleaseFrequency != .inherit {
attr = autoreleaseFrequency._attr(attr: attr)
}
// 这里也为false,不会执行
if #available(macOS 10.10, iOS 8.0, *), qos != .unspecified {
attr = __dispatch_queue_attr_make_with_qos_class(attr, qos.qosClass.rawValue, Int32(qos.relativePriority))
}
// 这里走required的初始化方法
if #available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) {
self.init(__label: label, attr: attr, queue: target)
} else {
self.init(__label: label, attr: attr)
if let tq = target { self.setTarget(queue: tq) }
}
}
继续看attributes._attr(),
public struct Attributes : OptionSet {
public let rawValue: UInt64
public init(rawValue: UInt64) { self.rawValue = rawValue }
public static let concurrent = Attributes(rawValue: 1<<1)
@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
public static let initiallyInactive = Attributes(rawValue: 1<<2)
fileprivate func _attr() -> __OS_dispatch_queue_attr? {
var attr: __OS_dispatch_queue_attr?
// 当队列为串行队列时,attr最终返回nil,
// 当队列为并行队列时,attr会被赋值
if self.contains(.concurrent) {
attr = _swift_dispatch_queue_concurrent()
}
// 表达式为false,不会执行
if #available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) {
if self.contains(.initiallyInactive) {
attr = __dispatch_queue_attr_make_initially_inactive(attr)
}
}
// 返回值为nil
return attr
}
}
至此,可以看出来,串行队列创建时,attr传入的值为nil,因此dpa也为空,所以串行队列返回的dpai是一个空的结构体,也就是里面的值都为0。
为了弄清楚并行队列的dpai的值的情况,我们还要再看回过头看一下swift的队列初始化方法,
convenience init(label: String,
qos: DispatchQoS = .unspecified,
attributes: DispatchQueue.Attributes = [],
autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = .inherit,
target: DispatchQueue? = nil)
从中很容易知道,qos为.unspecified,attributes可选值为.concurrent和.initiallyInactive,但默认都没有传,autoreleaseFrequency的值为.inherit,再结合dpqi中的参数,我们可以得到,
dqai.dqai_inactive = 0 // attributes没传入.initiallyInactive
dqai.dqai_concurrent = 1 // attributes传入.concurrent
dqai.dqai_relpri = ? // 依然不知道
dqai.dqai_qos = DISPATCH_QOS_UNSPECIFIED // 根据名字推断对应的是这个宏,这个宏代表的是0
dqai.dqai_autorelease_frequency = DISPATCH_AUTORELEASE_FREQUENCY_INHERIT // 根据名字推断对应的是这个宏,这个宏代表的是0
dqai.dqai_overcommit = 0 // 猜测应该为0,然后创建队列时,根据是串行队列还是并行队列,来确定过载(overcommit)参数的值
结语
由于能力有限,文章中难免出现疏漏或者根本性错误,欢迎指正。
如果文章对您有帮助,可以点个赞支持一下,谢谢!
接下来
现在我们已经知道一个队列是如何创建的了。我们也知道,当我们把一个异步任务添加到队列中的时候,这个添加操作不会阻塞当前线程,这个异步任务会在一段时间之后,在某个线程中被执行。 那么,这个异步任务被添加到哪里了呢?任务添加之后,什么时候执行呢?执行任务的线程时从哪里来的呢? 为了回答这些问题,在下一篇文章中,我们一起来看看dispatch_async的源码。