iOS 底層原理 - GCD

GCD簡介

什么是GCD?
全稱是 Grand Central Dispatch
純 C 語言,提供了非常多強大的函數(shù) GCD的優(yōu)勢
GCD 是蘋果公司為多核的并行運算提出的解決方案
GCD 會自動利用更多的CPU內核(比如雙核、四核)
GCD 會自動管理線程的生命周期(創(chuàng)建線程、調度任務、銷毀線程) 程序員只需要告訴 GCD 想要執(zhí)行什么任務,不需要編寫任何線程管理代碼

函數(shù)

將任務添加到隊列,并且指定執(zhí)行任務的函數(shù)
任務使用 block 封裝

  • 任務的 block 沒有參數(shù)也沒有返回值
  • 執(zhí)行任務的函數(shù)
  • 異步 dispatch_async
  • 不用等待當前語句執(zhí)行完畢,就可以執(zhí)行下一條語句 * 會開啟線程執(zhí)行 block 的任務
  • 異步是多線程的代名詞
  • 同步 dispatch_sync
  • 必須等待當前語句執(zhí)行完畢,才會執(zhí)行下一條語
  • 不會開啟線程
  • 在當前執(zhí)行 block 的任務

隊列

屏幕快照 2020-08-19 下午11.11.05.png

隊列的創(chuàng)建

#define DISPATCH_TARGET_QUEUE_DEFAULT NULL

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);
}

其參數(shù)如下:

const char *label: 隊列的唯一標識符,可以傳空值。
dispatch_queue_attr_t attr: 標識隊列的類型,區(qū)分是串行隊列還是并發(fā)隊列。

DISPATCH_QUEUE_SERIAL: 串行隊列
DISPATCH_QUEUE_CONCURRENT: 并發(fā)隊列

串行隊列的創(chuàng)建方法

dispatch_queue_t queue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);

常用的主隊列就是串行隊列,dispatch_get_main_queue()

專門用在主線程調度任務的隊列,也稱UI隊列
不會再開啟線程
如果有任務執(zhí)行,再添加其他任務,會被堵塞

并發(fā)隊列的創(chuàng)建方法

dispatch_queue_t queue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);

常用的全局隊列就是并發(fā)隊列dispatch_get_global_queue(long identifier, unsigned long flags),它可以直接執(zhí)行異步任務。該方法第一個參數(shù)是優(yōu)先級,全局隊列的優(yōu)先級為DISPATCH_QUEUE_PRIORITY_DEFAULT,這個值是一個為0的宏,所以也可以傳0。unsigned long flags: 蘋果官方文檔的解釋是Flags that are reserved for future use。標記這個參數(shù)是為了未來使用保留的,現(xiàn)在傳0即可。
此處引入線程的優(yōu)先級概念,優(yōu)先級越高越先執(zhí)行。

DISPATCH_QUEUE_PRIORITY_HIGH: 2
DISPATCH_QUEUE_PRIORITY_DEFAULT: 0
DISPATCH_QUEUE_PRIORITY_LOW: (-2)
DISPATCH_QUEUE_PRIORITY_BACKGROUND: INT16_MIN

隊列和函數(shù)組合

屏幕快照 2020-08-19 下午11.15.37.png

看下例子一

dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"1");
    // 異步函數(shù)
    dispatch_async(queue, ^{
        NSLog(@"2");
        NSLog(@"4");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
    });
    NSLog(@"5");

打印結果為1,5,2,4,3,先是順序執(zhí)行,異步隊列不會阻塞,但是會耗時,所以會慢些

例子二,選擇題

dispatch_queue_t queue = dispatch_queue_create("com.lg.cooci.cn", DISPATCH_QUEUE_CONCURRENT);

    /***
     1 2 3
     0
     7 8 9
     */
    
    dispatch_async(queue, ^{
        // sleep(2);
        NSLog(@"1");
    });
    dispatch_async(queue, ^{
        NSLog(@"2");
    });
    // 堵塞 - 護犢子
    dispatch_sync(queue, ^{
        NSLog(@"3");
    });
    // **********************
    NSLog(@"0");

    dispatch_async(queue, ^{
        NSLog(@"7");
    });
    dispatch_async(queue, ^{
        NSLog(@"8");
    });
    dispatch_async(queue, ^{
        NSLog(@"9");
    });

    // A: 1230789
    // B: 1237890
    // C: 3120798
    // D: 2137890

答案AC,1,2開啟異步線程,代碼在137行堵塞,阻塞耗時比異步隊列耗時久,所以0在1,2,3之后運行,0在主線程,7,8,9在它之后開啟異步線程,耗時,所以0在7,8,9之前

例子三

 // 同步隊列
    dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1");
    // 異步函數(shù)
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");

打印結果,1,5,2,產(chǎn)生死鎖崩潰,首先是一個串行隊列,1,5線執(zhí)行,然后執(zhí)行異步函數(shù)里的任務,2,同步代碼塊,4,在4這里產(chǎn)生阻塞,需要等同步代碼塊執(zhí)行完,同步代碼塊里有任務3,但是串行隊列的原則是先進先出,所以3需要等4任務執(zhí)行完后才能執(zhí)行,產(chǎn)生死鎖。


屏幕快照 2020-08-20 上午12.10.28.png

如果把4任務提前會死鎖嗎

// 同步隊列
    dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1");
    // 異步函數(shù)
    dispatch_async(queue, ^{
        NSLog(@"2");
        NSLog(@"4");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        
    });
    NSLog(@"5");

打印結果 1,5,2,4,崩潰,因為任務3和異步函數(shù)形成相互等待,使用方面要特別注意串行隊列和同步函數(shù)。判斷是否發(fā)生死鎖的最好方法就是看有沒有在串行隊列(當然也包括主隊列)中向這個隊列添加任務。

死鎖

? 主線程因為你同步函數(shù)的原因等著先執(zhí)行任務
? 主隊列等著主線程的任務執(zhí)行完畢再執(zhí)行自己的任務
? 主隊列和主線程相互等待會造成死鎖

隊列是如何創(chuàng)建的

我們通過dispatch_queue_create創(chuàng)建隊列,我們點進去發(fā)現(xiàn)看不到其他的,需要配合源碼分析,首先我們在dispatch_queue_create處下一個dispatch_queue_create的符號斷點,可以看到它在libdispatch動態(tài)庫里面,在Apple open source里下載對應的源碼。

屏幕快照 2020-08-21 上午7.56.43.png

打開源碼,搜索dispatch_queue_create( 發(fā)現(xiàn)搜出來很多,因為我們穿的第一個參數(shù)是字符串,搜索dispatch_queue_create(con,結果如下


屏幕快照 2020-08-21 上午8.03.09.png

來到這里

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

屏幕快照 2020-08-23 下午10.04.11.png

QOS:類似于優(yōu)先級,在創(chuàng)建global queue的時候的第一個參數(shù)就是priority或者QOS。

over commit:如果當前隊列沒有可以分配的空余線程,就開啟一個新線程來做任務

我們創(chuàng)建隊列主要是通過第二個參數(shù)區(qū)分是串行還是并行,也就是重點在于dispatch_queue_attr_t dqa這里。
搜索_dispatch_queue_attr_to_info方法,來到這里

ispatch_queue_attr_info_t
_dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa)
{
    dispatch_queue_attr_info_t dqai = { };
        //默認是串行隊列,如果傳NULL返回
    if (!dqa) return dqai;

#if DISPATCH_VARIANT_STATIC
    if (dqa == &_dispatch_queue_attr_concurrent) {
        dqai.dqai_concurrent = true;
        return dqai;
    }
#endif

    if (dqa < _dispatch_queue_attrs ||
            dqa >= &_dispatch_queue_attrs[DISPATCH_QUEUE_ATTR_COUNT]) {
        DISPATCH_CLIENT_CRASH(dqa->do_vtable, "Invalid queue attribute");
    }

    // 蘋果的算法
    size_t idx = (size_t)(dqa - _dispatch_queue_attrs);

    // 位域
    // 0000 000000000 00000000000 0000 000  1
    
    dqai.dqai_inactive = (idx % DISPATCH_QUEUE_ATTR_INACTIVE_COUNT);
    idx /= DISPATCH_QUEUE_ATTR_INACTIVE_COUNT;

    dqai.dqai_concurrent = !(idx % DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT);
    idx /= DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT;

    dqai.dqai_relpri = -(idx % DISPATCH_QUEUE_ATTR_PRIO_COUNT);
    idx /= DISPATCH_QUEUE_ATTR_PRIO_COUNT;

    dqai.dqai_qos = idx % DISPATCH_QUEUE_ATTR_QOS_COUNT;
    idx /= DISPATCH_QUEUE_ATTR_QOS_COUNT;

    dqai.dqai_autorelease_frequency =
            idx % DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;
    idx /= DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;

    dqai.dqai_overcommit = idx % DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;
    idx /= DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;

    return dqai;
}

第一句代碼就是將我們的外界傳進來區(qū)分串行還是并發(fā)的參數(shù)傳進去創(chuàng)建了一個 dispatch_queue_attr_info_t 類型的結構體。
一查 dispatch_queue_attr_info_t 是一個結構體位域,結構體位域可以通過一些位運算取出我們想要的內容,過濾掉我們不想要的數(shù)據(jù)。

1.串行隊列傳的是NULL ,所以直接返回.
2.并行隊列,通過按位取余設置下面的各個參數(shù)

typedef struct dispatch_queue_attr_info_s {
    dispatch_qos_t dqai_qos : 8;
    int      dqai_relpri : 8;
    uint16_t dqai_overcommit:2;
    uint16_t dqai_autorelease_frequency:2;
    uint16_t dqai_concurrent:1;
    uint16_t dqai_inactive:1;
} dispatch_queue_attr_info_t;

回到原來的方法_dispatch_lane_create_with_target繼續(xù)分析

static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
        dispatch_queue_t tq, bool legacy)
{
    ////其實就是把attr轉化為{}字典形式的attr集合,dqa里面會有qos,overcommit,inactive,concurrent之類的key和value
    dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

    //
    // Step 1: Normalize arguments (qos, overcommit, tq)
    //

    dispatch_qos_t qos = dqai.dqai_qos;
#if !HAVE_PTHREAD_WORKQUEUE_QOS
    if (qos == DISPATCH_QOS_USER_INTERACTIVE) {
        dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;
    }
    if (qos == DISPATCH_QOS_MAINTENANCE) {
        dqai.dqai_qos = qos = DISPATCH_QOS_BACKGROUND;
    }
#endif // !HAVE_PTHREAD_WORKQUEUE_QOS

    _dispatch_queue_attr_overcommit_t overcommit = dqai.dqai_overcommit;
    if (overcommit != _dispatch_queue_attr_overcommit_unspecified && tq) {
        if (tq->do_targetq) {
            DISPATCH_CLIENT_CRASH(tq, "Cannot specify both overcommit and "
                    "a non-global target queue");
        }
    }

    if (tq && dx_type(tq) == DISPATCH_QUEUE_GLOBAL_ROOT_TYPE) {
        // Handle discrepancies between attr and target queue, attributes win
        if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
            if (tq->dq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) {
                overcommit = _dispatch_queue_attr_overcommit_enabled;
            } else {
                overcommit = _dispatch_queue_attr_overcommit_disabled;
            }
        }
        if (qos == DISPATCH_QOS_UNSPECIFIED) {
            qos = _dispatch_priority_qos(tq->dq_priority);
        }
        tq = NULL;
    } else if (tq && !tq->do_targetq) {
        // target is a pthread or runloop root queue, setting QoS or overcommit
        // is disallowed
        if (overcommit != _dispatch_queue_attr_overcommit_unspecified) {
            DISPATCH_CLIENT_CRASH(tq, "Cannot specify an overcommit attribute "
                    "and use this kind of target queue");
        }
    } else {
        if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
            // Serial queues default to overcommit!
            overcommit = dqai.dqai_concurrent ?
                    _dispatch_queue_attr_overcommit_disabled :
                    _dispatch_queue_attr_overcommit_enabled;
        }
    }
    if (!tq) {
        tq = _dispatch_get_root_queue(
                qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos, // 4
                overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq; // 0 1
        if (unlikely(!tq)) {
            DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
        }
    }

    //
    // Step 2: Initialize the queue
    //

    if (legacy) {
        // if any of these attributes is specified, use non legacy classes
        if (dqai.dqai_inactive || dqai.dqai_autorelease_frequency) {
            legacy = false;
        }
    }

    const void *vtable;
    dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
    if (dqai.dqai_concurrent) {
        // 通過dqai.dqai_concurrent 來區(qū)分并發(fā)和串行
        // OS_dispatch_queue_concurrent_class
        vtable = DISPATCH_VTABLE(queue_concurrent);
    } else {
        vtable = DISPATCH_VTABLE(queue_serial);
    }
    switch (dqai.dqai_autorelease_frequency) {
    case DISPATCH_AUTORELEASE_FREQUENCY_NEVER:
        dqf |= DQF_AUTORELEASE_NEVER;
        break;
    case DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM:
        dqf |= DQF_AUTORELEASE_ALWAYS;
        break;
    }
    if (label) {
        const char *tmp = _dispatch_strdup_if_mutable(label);
        if (tmp != label) {
            dqf |= DQF_LABEL_NEEDS_FREE;
            label = tmp;
        }
    }
    
    // 開辟內存 - 生成響應的對象 queue
    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s));
    
    // 構造方法
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));

    // 標簽
    dq->dq_label = label;
    // 優(yōu)先級
    dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos,
            dqai.dqai_relpri);
    if (overcommit == _dispatch_queue_attr_overcommit_enabled) {
        dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
    }
    if (!dqai.dqai_inactive) {
        _dispatch_queue_priority_inherit_from_target(dq, tq);
        _dispatch_lane_inherit_wlh_from_target(dq, tq);
    }
    _dispatch_retain(tq);
    dq->do_targetq = tq;
    _dispatch_object_debug(dq, "%s", __func__);
    return _dispatch_trace_queue_create(dq)._dq;
}

通過dqai.dqai_concurrent 來區(qū)分并發(fā)和串行。_dispatch_queue_attr_overcommit_enabled串行,_dispatch_queue_attr_overcommit_disabled并發(fā)

overcommit = dqai.dqai_concurrent ?
                    _dispatch_queue_attr_overcommit_disabled :
                    _dispatch_queue_attr_overcommit_enabled;

dispatch_object_t

這里通過_dispatch_object_alloc開辟內存,那我們看一下dispatch_object_t的內容。
一般通過繼承實現(xiàn)多態(tài),這里通過聯(lián)合體的方式,包含了很多類型,避免開辟太多空間,是互斥的,同一時間只能有一個有效

typedef struct dispatch_object_s {
private:
    dispatch_object_s();
    ~dispatch_object_s();
    dispatch_object_s(const dispatch_object_s &);
    void operator=(const dispatch_object_s &);
} *dispatch_object_t;
#define DISPATCH_DECL(name) \
        typedef struct name##_s : public dispatch_object_s {} *name##_t
#define DISPATCH_DECL_SUBCLASS(name, base) \
        typedef struct name##_s : public base##_s {} *name##_t
#define DISPATCH_GLOBAL_OBJECT(type, object) (static_cast<type>(&(object)))
#define DISPATCH_RETURNS_RETAINED
#else /* Plain C */
#ifndef __DISPATCH_BUILDING_DISPATCH__
typedef union {
    struct _os_object_s *_os_obj;
    struct dispatch_object_s *_do;
    struct dispatch_queue_s *_dq;
    struct dispatch_queue_attr_s *_dqa;
    struct dispatch_group_s *_dg;
    struct dispatch_source_s *_ds;
    struct dispatch_mach_s *_dm;
    struct dispatch_mach_msg_s *_dmsg;
    struct dispatch_semaphore_s *_dsema;
    struct dispatch_data_s *_ddata;
    struct dispatch_io_s *_dchannel;
} dispatch_object_t DISPATCH_TRANSPARENT_UNION;

這里我們看一個例子。

// com.apple.root.default-qos width = 0xffe
    // com.apple.root.default-qos.overcommit   width = 0x1 = 1
    // 隊列 - 模板 _dispatch_root_queues里面取值
//    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
//        .dq_label = "com.apple.root.default-qos",
//        .dq_serialnum = 10,
//    ),
//    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,
//            DISPATCH_PRIORITY_FLAG_FALLBACK | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
//        .dq_label = "com.apple.root.default-qos.overcommit",
//        .dq_serialnum = 11,
//    ),

    // dispatch_init
    // _dispatch_root_queues init
    // width = 0xfff
    // 自定義: 0xffe
dispatch_queue_t queue1 = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_queue_t queue2 = dispatch_queue_create("KC", NULL);
    
    NSLog(@"%@",dispatch_get_main_queue());
    
    NSLog(@"%@",dispatch_get_global_queue(0, 0));

    NSLog(@"%@-%@",queue1,queue2);

打印結果如下

2020-08-23 22:47:45.560834+0800 001---函數(shù)與隊列[95788:1954758] 來了 master
2020-08-23 22:47:45.561110+0800 001---函數(shù)與隊列[95788:1954758] <OS_dispatch_queue_main: com.apple.main-thread>
2020-08-23 22:47:45.561291+0800 001---函數(shù)與隊列[95788:1954758] <OS_dispatch_queue_global: com.apple.root.default-qos>
2020-08-23 22:47:45.561472+0800 001---函數(shù)與隊列[95788:1954758] <OS_dispatch_queue_concurrent: cooci>-<OS_dispatch_queue_serial: KC>
(lldb) po queue1
<OS_dispatch_queue_concurrent: cooci[0x600003686600] = { xref = 1, ref = 1, sref = 1, target = com.apple.root.default-qos[0x105530f00], width = 0xffe, state = 0x0000041000000000, in-flight = 0}>

(lldb) po quere2
error: use of undeclared identifier 'quere2'
(lldb) po queue2
<OS_dispatch_queue_serial: KC[0x600003686700] = { xref = 1, ref = 1, sref = 1, target = com.apple.root.default-qos.overcommit[0x105530f80], width = 0x1, state = 0x001ffe2000000000, in-flight = 0}>

通過上面分析我們可以看到一個com.apple.root.default-qos,width = 0xffe,為并發(fā)隊列,一個com.apple.root.default-qos.overcommit,width = 0x1 = 1為串行隊列。DISPATCH_QUEUE_WIDTH_MAX是通過源碼知道是一個宏定義,等于0x1000減2,結果為0xffe。中間有一個宏DISPATCH_QUEUE_WIDTH_POOL 為0x1000減一,所以這里是減2。


屏幕快照 2020-09-22 下午10.25.32.png
// 構造方法
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));

_dispatch_get_root_queue

static inline dispatch_queue_global_t
_dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
{
    if (unlikely(qos < DISPATCH_QOS_MIN || qos > DISPATCH_QOS_MAX)) {
        DISPATCH_CLIENT_CRASH(qos, "Corrupted priority");
    }
    // 4-1= 3
    // 2*3+0/1 = 6/7
    return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}

通過上面的源碼可以看到_dispatch_get_root_queue方法的傳值,qos為4,overcommit為0,1,串行為1,并發(fā)為0,默認為1。

if (!tq) {
        tq = _dispatch_get_root_queue(
                qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos, // 4
                overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq; // 0 1
        if (unlikely(!tq)) {
            DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
        }
    }

這里其實是return _dispatch_root_queues[2 * (qos - 1) + overcommit]
也就是說target queue就是從_dispatch_root_queues隊列里面拿第(2 * (qos - 1) + overcommit)個queue。
我們接下來看下_dispatch_root_queues這個數(shù)組,在源碼里查找

struct dispatch_queue_global_s _dispatch_root_queues[] = {
#define _DISPATCH_ROOT_QUEUE_IDX(n, flags) \
        ((flags & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) ? \
        DISPATCH_ROOT_QUEUE_IDX_##n##_QOS_OVERCOMMIT : \
        DISPATCH_ROOT_QUEUE_IDX_##n##_QOS)
#define _DISPATCH_ROOT_QUEUE_ENTRY(n, flags, ...) \
    [_DISPATCH_ROOT_QUEUE_IDX(n, flags)] = { \
        DISPATCH_GLOBAL_OBJECT_HEADER(queue_global), \
        .dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE, \
        .do_ctxt = _dispatch_root_queue_ctxt(_DISPATCH_ROOT_QUEUE_IDX(n, flags)), \
        .dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL), \
        .dq_priority = flags | ((flags & DISPATCH_PRIORITY_FLAG_FALLBACK) ? \
                _dispatch_priority_make_fallback(DISPATCH_QOS_##n) : \
                _dispatch_priority_make(DISPATCH_QOS_##n, 0)), \
        __VA_ARGS__ \
    }
    _DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, 0,
        .dq_label = "com.apple.root.maintenance-qos",
        .dq_serialnum = 4,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.maintenance-qos.overcommit",
        .dq_serialnum = 5,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, 0,
        .dq_label = "com.apple.root.background-qos",
        .dq_serialnum = 6,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.background-qos.overcommit",
        .dq_serialnum = 7,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, 0,
        .dq_label = "com.apple.root.utility-qos",
        .dq_serialnum = 8,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.utility-qos.overcommit",
        .dq_serialnum = 9,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
        .dq_label = "com.apple.root.default-qos",
        .dq_serialnum = 10,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,
            DISPATCH_PRIORITY_FLAG_FALLBACK | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.default-qos.overcommit",
        .dq_serialnum = 11,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, 0,
        .dq_label = "com.apple.root.user-initiated-qos",
        .dq_serialnum = 12,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.user-initiated-qos.overcommit",
        .dq_serialnum = 13,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, 0,
        .dq_label = "com.apple.root.user-interactive-qos",
        .dq_serialnum = 14,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.user-interactive-qos.overcommit",
        .dq_serialnum = 15,
    ),
};

這里就拿到了上面的打印結果。

打印

    NSLog(@"%@",dispatch_get_main_queue());
    
    NSLog(@"%@",dispatch_get_global_queue(0, 0));

輸出結果為

2020-08-27 20:38:34.511876+0800 001---函數(shù)與隊列[96868:2126481] <OS_dispatch_queue_main: com.apple.main-thread>
2020-08-27 20:38:34.512160+0800 001---函數(shù)與隊列[96868:2126481] <OS_dispatch_queue_global: com.apple.root.default-qos>
(lldb) po dispatch_get_global_queue(0, 0)
<OS_dispatch_queue_global: com.apple.root.default-qos[0x109113f00] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>

dispatch_get_main_queue()組隊列可以直接輸出,是個全局靜態(tài)結構體,源碼搜索com.apple.main-thread,發(fā)現(xiàn)_dispatch_main_q如下,dq_serialnum為1是串行隊列

struct dispatch_queue_static_s _dispatch_main_q = {
    DISPATCH_GLOBAL_OBJECT_HEADER(queue_main),
#if !DISPATCH_USE_RESOLVERS
    .do_targetq = _dispatch_get_default_queue(true),
#endif
    .dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) |
            DISPATCH_QUEUE_ROLE_BASE_ANON,
    .dq_label = "com.apple.main-thread",
    .dq_atomic_flags = DQF_THREAD_BOUND | DQF_WIDTH(1),
    .dq_serialnum = 1,
};

這里有個宏DISPATCH_GLOBAL_OBJECT_HEADER,搜下看相當于這四句代碼

#if OS_OBJECT_HAVE_OBJC1
#define DISPATCH_GLOBAL_OBJECT_HEADER(name) \
    .do_vtable = DISPATCH_VTABLE(name), \
    ._objc_isa = DISPATCH_OBJC_CLASS(name), \
    .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, \
    .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT
#else
#define DISPATCH_GLOBAL_OBJECT_HEADER(name) \
    .do_vtable = DISPATCH_VTABLE(name), \
    .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, \
    .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT
#endif

繼續(xù)往下分析,打印dispatch_get_global_queue(0, 0)發(fā)現(xiàn)它的width = 0xfff,也就是0x1000減1,給了全局并發(fā)隊列
// dispatch_init
// _dispatch_root_queues init 初始化就是0xfff,為了區(qū)分自定義的,自定義的是0xffe,任何隊列都是由_dispatch_root_queues進行模板創(chuàng)建的,除去main_queue。
// width = 0xfff
// 自定義: 0xffe

總結一下各種queue的獲取方式吧:
自己create創(chuàng)建的queue是需要alloc分配內存以后init,最后從root_queue里面的拿一個作為新queue的target queue的
main和global queue是不需要alloc init的,直接從root_queue里拿出對應的queue即可

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容