上一回从public API的角度看了如何新建一个队列、哪些字段有关队列优先级、以及队列的优先级怎么设置。这一回就从源码的角度来证实上一回的猜测。
一、_dispatch_lane_create_with_target 探索
1.1 dispatch_get_main_queue()
- 转写成cpp文件:
xcrun -sdk iphonesimulator clang -arch x86_64 -rewrite-objc main.m
,得到以下信息:_dispatch_main_q
dispatch_queue_main_t
dispatch_get_main_queue(void)
{
return (( dispatch_queue_main_t)&(_dispatch_main_q));
}
- 在GCD源码中找到
_dispatch_main_q
的定义
struct dispatch_queue_static_s _dispatch_main_q = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_main),
#if !DISPATCH_USE_RESOLVERS
.do_targetq = _dispatch_get_default_queue(true),
#endif
.dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) |
DISPATCH_QUEUE_ROLE_BASE_ANON,
.dq_label = "com.apple.main-thread",
.dq_atomic_flags = DQF_THREAD_BOUND | DQF_WIDTH(1),
.dq_serialnum = 1,
};
- 第一行:
DISPATCH_GLOBAL_OBJECT_HEADER(queue_main)
,在GCD的类型定义的文章中解释的非常详细了; - 第二行:
.do_targetq = _dispatch_get_default_queue(true)
,在上一回的讲解中已经知道了,主队列仍然依赖于一个根队列,而这里便是对targetq的设置,最后会走到_dispatch_root_queues
方法中去,这个方法放在下面做讲解; - 第三行:
.dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) | DISPATCH_QUEUE_ROLE_BASE_ANON
,上一回的日志中就看到了这个属性,从这里的赋值来看,这个属性是一个比较大的数值,具体值的大小可以在源码中看到,主队列的默认值为:0x001ffe1000000000
,而最终输出的值不一样,说明后面修改过该值。
- 第四行:
.dq_label = "com.apple.main-thread"
,主队列的标识,跟输出的结果一致; - 第五行:
.dq_atomic_flags = DQF_THREAD_BOUND | DQF_WIDTH(1)
,可能跟线程安全相关; - 第六行:
.dq_serialnum = 1
,很简单的猜测为串行队列,任务数为1;
1.2 dispatch_get_global_queue()
在源码中找到dispatch_get_global_queue
的定义。代码逻辑非常简单,确定队列的优先级,然后根据优先级获取一个根队列。
第一行:flags & ~(unsigned long)DISPATCH_QUEUE_OVERCOMMIT
,做了一个限制,保证任何非0数都返回一个NULL。(实测2是可以通过的,可能是将来预留的某种标识,实际中别使用,玩源码的时候可以测试下)。
最后根据_dispatch_get_root_queue
,返回一个根队列,函数的第二个参数很有意思,flag=0
,那么这个参数就定为false,也即并行队列。
dispatch_queue_global_t
dispatch_get_global_queue(intptr_t priority, uintptr_t flags)
{
dispatch_assert(countof(_dispatch_root_queues) ==
DISPATCH_ROOT_QUEUE_COUNT);
if (flags & ~(unsigned long)DISPATCH_QUEUE_OVERCOMMIT) {
return DISPATCH_BAD_INPUT;
}
dispatch_qos_t qos = _dispatch_qos_from_queue_priority(priority);
#if !HAVE_PTHREAD_WORKQUEUE_QOS
if (qos == QOS_CLASS_MAINTENANCE) {
qos = DISPATCH_QOS_BACKGROUND;
} else if (qos == QOS_CLASS_USER_INTERACTIVE) {
qos = DISPATCH_QOS_USER_INITIATED;
}
#endif
if (qos == DISPATCH_QOS_UNSPECIFIED) {
return DISPATCH_BAD_INPUT;
}
return _dispatch_get_root_queue(qos, flags & DISPATCH_QUEUE_OVERCOMMIT);
}
1.3 dispatch_queue_create()
dispatch_queue_create
方法最终走到了_dispatch_lane_create_with_target
方法。
前面两个参数:label
、attr
上一回讲过了,并会透传到下层方法;
后面两个参数:tq:DISPATCH_TARGET_QUEUE_DEFAULT
表示新建一个队列的默认targetq是默认优先级的,legacy:true
表示使用遗留类。具体的含义会在_dispatch_lane_create_with_target 流程
中讲解。
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);
}
二、_dispatch_lane_create_with_target 流程
对源码取出一些条件判断,取出主流程,这里忽略的legacy
参数相关逻辑,这个参数主要是控制队列的释放条件。剩余部分如下所示:
static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
dispatch_queue_t tq, bool legacy)
{
// 获取队列属性描述
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
// 步骤 1:通用参数设置
// 关于 qos 的一些判断条件省略,直接取重点流程
dispatch_qos_t qos = dqai.dqai_qos;
_dispatch_queue_attr_overcommit_t overcommit = dqai.dqai_overcommit;
// 串行队列默认是 overcommit
overcommit = dqai.dqai_concurrent ?
_dispatch_queue_attr_overcommit_disabled :
_dispatch_queue_attr_overcommit_enabled;
// 设置 targetq
tq = _dispatch_get_root_queue(
qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT/*4*/ : qos,
overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq;
// 步骤 2:初始化队列
const void *vtable;
// 设置队列标识
dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
// 设置是串行队列还是并行队列
if (dqai.dqai_concurrent) {
vtable = DISPATCH_VTABLE(queue_concurrent);
} else {
vtable = DISPATCH_VTABLE(queue_serial);
}
// 配置队列的标签
if (label) {
const char *tmp = _dispatch_strdup_if_mutable(label);
label = tmp;
}
// 分配队列内存
dispatch_lane_t dq = _dispatch_object_alloc(vtable,
sizeof(struct dispatch_lane_s));
// 初始化队列
_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_label = label;
// 设置队列优先级
dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos,
dqai.dqai_relpri);
if (overcommit == _dispatch_queue_attr_overcommit_enabled) {
dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
}
// 设置并持有目标队列
_dispatch_retain(tq);
dq->do_targetq = tq;
// 返回队列
return _dispatch_trace_queue_create(dq)._dq;
}
2.1 _dispatch_queue_attr_to_info(dqa)
dqa
就是通过dispatch_queue_create
的第二个参数透传过来的值。这个属性包含的值也是相当有限的,但在创建队列的过程中,非常依赖由dqa
这个值生成的结果dqai
,说明GCD中的属性之间会有比较多的关联。例如:dqai_concurrent
可以确定overcommit
属性,而overcommit
的值又会影响dq_priority
属性。而苹果将它们都通过计算存在一个值里面。可以看一下它的赋值过程:
size_t idx = (size_t)(dqa - _dispatch_queue_attrs);
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;
有兴趣的话,可以每个值都替换进去,看一看类型的变化。
2.2 通用参数设置
这一步主要设置以下三个属性:
qos = dqai.dqai_qos
- 外部传进来的 attr;
- 将 attr 中的优先级保存进
info 中 qos
字段; - 取 info 中的 qos;
overcommit = dqai.dqai_concurrent ? _dispatch_queue_attr_overcommit_disabled : _dispatch_queue_attr_overcommit_enabled
- 外部传进来的 attr;
- 将 attr 中串行/并行属性保存进
info 中 dqai_concurrent
字段; - 根据
info 中 dqai_concurrent
决定 overcommit 字段。并行队列:disabled,串行队列:enabled
tq = _dispatch_get_root_queue( qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT/*4*/ : qos, overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq;
- 根据上面得到的
qos
和overcommit
来获取 target; - 走到
_dispatch_get_root_queue
流程;
2.3 初始化队列
- 分配内存。这个方法最终会调到:
malloc_zone_calloc(_dispatch_ccache_zone, (n), (s))
dispatch_lane_t dq = _dispatch_object_alloc(vtable,
sizeof(struct dispatch_lane_s));
- 初始化队列。内部会根据几个参数对队列的属性进行初始化。
_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));
- 持有tq。targetq引用计数+1,targetq指针指向tq。
// 设置并持有目标队列
_dispatch_retain(tq);
dq->do_targetq = tq;
三、dispatch_queue_attr_info_t 详解
typedef struct dispatch_queue_attr_info_s {
dispatch_qos_t dqai_qos : 8;
int dqai_relpri : 8;
uint16_t dqai_overcommit:2;
uint16_t dqai_autorelease_frequency:2;
uint16_t dqai_concurrent:1;
uint16_t dqai_inactive:1;
} dispatch_queue_attr_info_t;
- dqai_qos:queue的target的优先级
- dqai_relpri:关联优先级。queue在target中的优先级
- dqai_overcommit:是否可以overcommit。依赖于dqai_concurrent属性,串行队列:overcommit为true,并行队列为false
- dqai_autorelease_frequency:自动释放频率
- dqai_concurrent:是否是并行队列
- dqai_inactive:是否激活
下面可以静态感受一下苹果对 info 的赋值:
- 取出一个相对下标;
- 然后对每个段辗转取余;
- 取完某个段的值之后便将其移除;
size_t idx = (size_t)(dqa - _dispatch_queue_attrs);
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;
上面的宏定义:
#define DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT 3
#define DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT 3
#define DISPATCH_QUEUE_ATTR_QOS_COUNT (DISPATCH_QOS_MAX + 1)
#define DISPATCH_QUEUE_ATTR_PRIO_COUNT (1 - QOS_MIN_RELATIVE_PRIORITY)
#define DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT 2
#define DISPATCH_QUEUE_ATTR_INACTIVE_COUNT 2
#define DISPATCH_QUEUE_ATTR_COUNT ( \
DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT * \
DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT * \
DISPATCH_QUEUE_ATTR_QOS_COUNT * \
DISPATCH_QUEUE_ATTR_PRIO_COUNT * \
DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT * \
DISPATCH_QUEUE_ATTR_INACTIVE_COUNT )
#define DISPATCH_QOS_UNSPECIFIED ((dispatch_qos_t)0)
#define DISPATCH_QOS_MAINTENANCE ((dispatch_qos_t)1)
#define DISPATCH_QOS_BACKGROUND ((dispatch_qos_t)2)
#define DISPATCH_QOS_UTILITY ((dispatch_qos_t)3)
#define DISPATCH_QOS_DEFAULT ((dispatch_qos_t)4)
#define DISPATCH_QOS_USER_INITIATED ((dispatch_qos_t)5)
#define DISPATCH_QOS_USER_INTERACTIVE ((dispatch_qos_t)6)
#define DISPATCH_QOS_MIN DISPATCH_QOS_MAINTENANCE
#define DISPATCH_QOS_MAX DISPATCH_QOS_USER_INTERACTIVE
#define DISPATCH_QOS_SATURATED ((dispatch_qos_t)15)
typedef unsigned long pthread_priority_t;
#define QOS_MIN_RELATIVE_PRIORITY (-15)
#define _PTHREAD_PRIORITY_FLAGS_MASK (~0xffffff)
#define _PTHREAD_PRIORITY_QOS_CLASS_MASK 0x00ffff00
#define _PTHREAD_PRIORITY_QOS_CLASS_SHIFT (8ull)
#define _PTHREAD_PRIORITY_PRIORITY_MASK 0x000000ff
#define _PTHREAD_PRIORITY_OVERCOMMIT_FLAG 0x80000000
#define _PTHREAD_PRIORITY_SCHED_PRI_FLAG 0x20000000
#define _PTHREAD_PRIORITY_FALLBACK_FLAG 0x04000000
#define _PTHREAD_PRIORITY_EVENT_MANAGER_FLAG 0x02000000
#define _PTHREAD_PRIORITY_NEEDS_UNBIND_FLAG 0x01000000
#define _PTHREAD_PRIORITY_ENFORCE_FLAG 0x10000000
除了GCD内部调用的方法是这样,上一回讲到的dispatch_queue_attr_make_with_qos_class
方法中也是用的类似的方法给属性赋值,包括另外两个未展开讲解的方法也是一样:
dispatch_queue_attr_make_initially_inactive
dispatch_queue_attr_make_with_autorelease_frequency
下面附上这三个方法的源码:
dispatch_queue_attr_t
dispatch_queue_attr_make_with_qos_class(dispatch_queue_attr_t dqa,
dispatch_qos_class_t qos_class, int relpri)
{
if (!_dispatch_qos_class_valid(qos_class, relpri)) {
return (dispatch_queue_attr_t)dqa;
}
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
dqai.dqai_qos = _dispatch_qos_from_qos_class(qos_class);
dqai.dqai_relpri = relpri;
return _dispatch_queue_attr_from_info(dqai);
}
dispatch_queue_attr_t
dispatch_queue_attr_make_initially_inactive(dispatch_queue_attr_t dqa)
{
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
dqai.dqai_inactive = true;
return _dispatch_queue_attr_from_info(dqai);
}
dispatch_queue_attr_t
dispatch_queue_attr_make_with_autorelease_frequency(dispatch_queue_attr_t dqa,
dispatch_autorelease_frequency_t frequency)
{
switch (frequency) {
case DISPATCH_AUTORELEASE_FREQUENCY_INHERIT:
case DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM:
case DISPATCH_AUTORELEASE_FREQUENCY_NEVER:
break;
}
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
dqai.dqai_autorelease_frequency = (uint16_t)frequency;
return _dispatch_queue_attr_from_info(dqai);
}
到这里,队列中的属性就是非常明了了。
【补充】dispatch_autorelease_frequency
关于create流程中legacy参数确定的取值
DISPATCH_ENUM(dispatch_autorelease_frequency, unsigned long,
DISPATCH_AUTORELEASE_FREQUENCY_INHERIT DISPATCH_ENUM_API_AVAILABLE(
macos(10.12), ios(10.0), tvos(10.0), watchos(3.0)) = 0,
DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM DISPATCH_ENUM_API_AVAILABLE(
macos(10.12), ios(10.0), tvos(10.0), watchos(3.0)) = 1,
DISPATCH_AUTORELEASE_FREQUENCY_NEVER DISPATCH_ENUM_API_AVAILABLE(
macos(10.12), ios(10.0), tvos(10.0), watchos(3.0)) = 2,
);
- DISPATCH_AUTORELEASE_FREQUENCY_INHERIT
继承目标队列的释放频率
与目标队列的释放频率保持一致 - DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM
在每个block被异步添加到队列中的时候push
在每个block执行完成之后pop - DISPATCH_AUTORELEASE_FREQUENCY_NEVER
这种类型只针对全局队列
并且不支持自定义的自动释放池的push和pop
四、_dispatch_root_queues 详解
_dispatch_root_queues
源码如下:
-
_DISPATCH_ROOT_QUEUE_IDX
宏定义是获取根队列的最终类型; -
_DISPATCH_ROOT_QUEUE_ENTRY
宏定义是给这个根队列中的属性进行赋值; - 从第三个代码块到最后一个代码块,一起12个代码块。
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,
),
};
- 这里的是12条系统维护的根队列
- 每一种优先级的队列都有串行和并行两种
- 也就是有6种,这六种优先从上到下递减
__QOS_ENUM(qos_class, unsigned int, QOS_CLASS_USER_INTERACTIVE __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x21, QOS_CLASS_USER_INITIATED __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x19, QOS_CLASS_DEFAULT __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x15, QOS_CLASS_UTILITY __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x11, QOS_CLASS_BACKGROUND __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x09, QOS_CLASS_UNSPECIFIED __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x00, );
- dispatch_get_main_queue
com.apple.root.default-qos.overcommit QOS_CLASS_DEFAULT
- dispatch_get_global_queue
com.apple.root.default-qos QOS_CLASS_DEFAULT