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

相关推荐
QGC二次开发2 分钟前
Vue3 : Pinia的性质与作用
前端·javascript·vue.js·typescript·前端框架·vue
云草桑13 分钟前
逆向工程 反编译 C# net core
前端·c#·反编译·逆向工程
布丁椰奶冻18 分钟前
解决使用nvm管理node版本时提示npm下载失败的问题
前端·npm·node.js
Leyla44 分钟前
【代码重构】好的重构与坏的重构
前端
影子落人间1 小时前
已解决npm ERR! request to https://registry.npm.taobao.org/@vant%2farea-data failed
前端·npm·node.js
世俗ˊ1 小时前
CSS入门笔记
前端·css·笔记
子非鱼9211 小时前
【前端】ES6:Set与Map
前端·javascript·es6
6230_1 小时前
git使用“保姆级”教程1——简介及配置项设置
前端·git·学习·html·web3·学习方法·改行学it
想退休的搬砖人1 小时前
vue选项式写法项目案例(购物车)
前端·javascript·vue.js
加勒比海涛2 小时前
HTML 揭秘:HTML 编码快速入门
前端·html