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底层分析流程如下图所示

相关推荐
Cao_Ron4 分钟前
用 SVG image symbol 画 ECharts graph 圆形节点
前端
hxy06018 分钟前
React-Call让你的React组件像异步函数一样调用
前端·react.js
Hilaku9 分钟前
为什么 AI 写前端时,总是优先选择 React,而不是 Vue?
前端·javascript·程序员
京东云开发者11 分钟前
GPT-5.6、ChatGPT Work 与 Codex 更新食用指南
前端
Cao_Ron22 分钟前
用 ECharts graph 画出卡片式矩形节点
前端
skiyee31 分钟前
换个底座的 Wot Starter 居然让 AI 更懂项目!
前端·ai编程
anyup37 分钟前
这一套绝了!AI 大模型写故事,星云 SDK 驱动 3D 数字人实时演绎
前端·人工智能·aigc
耀耀切克闹灬1 小时前
短链跳转
前端
孟陬1 小时前
高质量全方位的 AI 工具 sse-stuntman — Mock SSE 接口
前端·node.js·openai
Cao_Ron1 小时前
给 ECharts 6 graph 节点叠加角标:从原理到性能优化
前端