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

相关推荐
桂月二二4 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
hunter2062065 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb5 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角5 小时前
CSS 颜色
前端·css
浪浪山小白兔6 小时前
HTML5 新表单属性详解
前端·html·html5
lee5767 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579657 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter
limit for me7 小时前
react上增加错误边界 当存在错误时 不会显示白屏
前端·react.js·前端框架
浏览器爱好者7 小时前
如何构建一个简单的React应用?
前端·react.js·前端框架
qq_392794488 小时前
前端缓存策略:强缓存与协商缓存深度剖析
前端·缓存