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

相关推荐
小小小小宇10 分钟前
LLM 长期记忆构建
前端
lichenyang45322 分钟前
从 Express 老项目到 NestJS + Docker:一次车辆管理系统的渐进式重构
前端
Momo__2 小时前
VueUse createReusableTemplate —— 单文件组件内的模板复用神器
前端·vue.js
程序员小富2 小时前
我开源了一个开发者专属的智能 JSON 工具,得到了媳妇高度认可
前端·vue.js·后端
小小小小宇2 小时前
程序员如何给 LLM 装工具以及看懂推理过程
前端
写代码的皮筏艇2 小时前
React中的forwardRef
前端·react.js·面试
槑有老呆2 小时前
花三个月工资请了个 AI 程序员,结果它连青岛啤酒股价都查不了
前端
风骏时光牛马2 小时前
Verilog开发常见问题汇总解析
前端
子兮曰2 小时前
AI Coding Method Map:一张图看懂 AI 编程的完整链路
前端·人工智能·后端