iOS GCD 底层源码分析

创建队列底层源码分析

通过上篇文章 iOS 深入了解 GCD 函数和队列中,我们了解函数和队列,知道队列的创建时通过 GCD 中的dispatch_queue_create方法,下面我们在libdispatch.dylib(libdispatch-1173.60.1) 去探索队列是如何创建的(源码下载)

  • 搜索 dispatch_queue_create
arduino 复制代码
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
scss 复制代码
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, tq
    ...
    
    // 拼接队列名称
    const void *vtable;
    dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
    if (dqai.dqai_concurrent) { //vtable表示类的类型
        // OS_dispatch_queue_concurrent
        vtable = DISPATCH_VTABLE(queue_concurrent);
    } else {
        vtable = DISPATCH_VTABLE(queue_serial);
    }
    
    ....
    
    // 开辟内存 生成响应的对象 queue
    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s)); // alloc
    // 构造方法        
    // 根据 dqai.dqai_concurrent 的值,就能判断队列是串行还是并发
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0)); // init
    // 设置队列 label 标识符
    dq->dq_label = label;
    // 优先级处理
    dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos, dqai.dqai_relpri);
    
    ...
    
    // 类似于类与元类的绑定,不是直接的继承关系,而是类似于模型与模板的关系
    dq->do_targetq = tq;
    _dispatch_object_debug(dq, "%s", __func__);
    return _dispatch_trace_queue_create(dq)._dq;
}

【第一步】第6行通过_dispatch_queue_attr_to_info方法传入dqa(即队列类型,串行、并发等)创建dispatch_queue_attr_info_t类型的对象dqai,用于存储队列的相关属性信息

_dispatch_queue_attr_to_info 方法代码如下:

【第二步】看423 ~ 430行,判断当前传入的 dqa 是串行(NULL)还是并发。如果是串行(NULL)就直接返回 dqa,否则就继续往下走看432 ~ 460行。438行是苹果的算法,441 ~ 458行是位域。

【第三步】看16行通过DISPATCH_VTABLE拼接队列名称,即vtable。其中DISPATCH_VTABLE是宏定义,如下所示,队列的类型是通过OS_dispatch_+ 队列类型queue_concurrent拼接而成的。

  • 串行队列类型:OS_dispatch_queue_serial,验证如下
  • 并发队列类型:OS_dispatch_queue_concurrent,验证如下
scss 复制代码
#define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)
#define DISPATCH_OBJC_CLASS(name)   (&DISPATCH_CLASS_SYMBOL(name))
#define DISPATCH_CLASS(name) OS_dispatch_##name

【第四步】看26 ~ 30行通过_dispatch_object_alloc + _dispatch_queue_init初始化队列,即dq。其中在_dispatch_queue_init传参中根据dqai.dqai_concurrent的布尔值,就能判断队列是串行还是并发,而vtable表示队列的类型,说明队列也是对象

  • 进入_dispatch_object_alloc -> _os_object_alloc_realized方法中设置了 isa 的指向,从这里可以验证队列也是对象的说法。

_dispatch_object_alloc 方法代码如下:

_os_object_alloc_realized 方法代码如下:

  • 进入_dispatch_queue_init方法,队列类型是dispatch_queue_t,并设置队列的相关属性。

【第五步】通过_dispatch_trace_queue_create对创建的队列进行处理,其中_dispatch_trace_queue_create_dispatch_introspection_queue_create封装的宏定义,最后会返回处理过的_dq

进入_dispatch_introspection_queue_create_hook -> dispatch_introspection_queue_get_info -> _dispatch_introspection_lane_get_info中可以看出,与我们自定义的类还是有所区别的,创建队列在底层的实现是通过模板创建的。

总结

  • 队列创建方法dispatch_queue_create中的参数二(即队列类型), 决定了下层中 max & 1(用于区分是串行还是并发), 其中1表示串行
  • queue也是一个对象,也需要通过底层 alloc + init 创建, 并且在 alloc 中也有一个class, 这个class是通过宏定义拼接而成,并且同时会指定isa的指向
  • 创建队列在底层的处理是通过模板创建的,其类型是dispatch_introspection_queue_s结构体。

dispatch_queue_create底层分析流程如下图所示

相关推荐
m0_7482402520 分钟前
前端如何检测用户登录状态是否过期
前端
black^sugar22 分钟前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人1 小时前
前端知识补充—CSS
前端·css
GISer_Jing1 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_748245521 小时前
吉利前端、AI面试
前端·面试·职场和发展
理想不理想v1 小时前
webpack最基础的配置
前端·webpack·node.js
pubuzhixing2 小时前
开源白板新方案:Plait 同时支持 Angular 和 React 啦!
前端·开源·github
2401_857600952 小时前
SSM 与 Vue 共筑电脑测评系统:精准洞察电脑世界
前端·javascript·vue.js
2401_857600952 小时前
数字时代的医疗挂号变革:SSM+Vue 系统设计与实现之道
前端·javascript·vue.js
GDAL2 小时前
vue入门教程:组件透传 Attributes
前端·javascript·vue.js