GCD源碼分析(上)

GCD源碼官網(wǎng)下載地址

GCD的源碼在我看來姜挺,一直是iOS源碼中最晦澀難懂一種,因為涉及到太多的宏定義和結構定義,并且沒有多少關于GCD源碼的文章作為參考咧党,我只能根據(jù)自己的理解來粗略進行分析,如果有哪些錯誤或者不足之處陨亡,希望以后看到本篇文章的大佬能夠留言指出傍衡,謝謝深员!

一. 從打印開始

我們先來看一段常寫代碼,并分析其屬性

- (void)testQueue{
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"serialQueue:%@",serialQueue);
    
    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"concurrentQueue:%@",concurrentQueue);
    
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    NSLog(@"mainQueue:%@",mainQueue);
    
    dispatch_queue_t default_globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSLog(@"default_globalQueue:%@",default_globalQueue);
    
    dispatch_queue_t low_globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    NSLog(@"low_globalQueue:%@",low_globalQueue);
    
    dispatch_queue_t high_globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    NSLog(@"high_globalQueue:%@",high_globalQueue);
}
打印結果
serialQueue:<OS_dispatch_queue_serial: serialQueue>
concurrentQueue:<OS_dispatch_queue_concurrent: concurrentQueue>
mainQueue:<OS_dispatch_queue_main: com.apple.main-thread>
default_globalQueue:<OS_dispatch_queue_global: com.apple.root.default-qos>
low_globalQueue:<OS_dispatch_queue_global: com.apple.root.utility-qos>
high_globalQueue:<OS_dispatch_queue_global: com.apple.root.user-initiated-qos>

我們在將斷點加在最后蛙埂,po一下具體的內(nèi)部屬性


po結果.png

我們知道倦畅,主隊列也是串行隊列,全局隊列也是并行隊列绣的,從po出的信息來看他們的共性

  • 主隊列和串行隊列的width0x1,而并行隊列的width都大于1
  • 全局隊列的target都是[0x0]叠赐,串行隊列的target都顯示的是com.apple.root.default-qos.overcommit[0x103021f80],并行隊列的target為com.apple.root.default-qos[0x103021f00]

據(jù)此屡江,我們可以初步得出結論:

  1. 串行隊列的寬度(width)為1芭概,并行隊列的寬度大于1
  2. 全局隊列的target為[0x0],其他隊列的target以com.apple.root.default-開頭
  3. 自己創(chuàng)建的串行隊列和并行隊列都使用自己創(chuàng)建時給的名字惩嘉,而主隊列和全局隊列罢洲,都有一個系統(tǒng)給的名字,主隊列名為com.apple.main-thread宏怔,全局默認優(yōu)先級名為com.apple.root.default-qos奏路,全局低優(yōu)先級名為com.apple.root.utility-qos,全局高優(yōu)先級名為com.apple.root.user-initiated-qos

接下來我們從源碼開始分析

二臊诊、從隊列創(chuàng)建開始

我們從dispatch_queue_create函數(shù)作為切入開始查詢源碼

dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
{
   //調(diào)用_dispatch_lane_create_with_target
    return _dispatch_lane_create_with_target(label, attr,
            DISPATCH_TARGET_QUEUE_DEFAULT, true);
}
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)
{
    
    //1. 第一步 設置dispatch_queue_attr_info_t屬性
    /*
     通過傳入的隊列屬性鸽粉,設置隊列信息
     串行隊列 返回的是空字典
     非串行隊列 表示叫
    */
    dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

    /*
     規(guī)范化各個參數(shù),如(qos, overcommit, tq)
     */
    dispatch_qos_t qos = dqai.dqai_qos;
#if !HAVE_PTHREAD_WORKQUEUE_QOS
    //如果qos等于6抓艳,那么將值設置為5
    if (qos == DISPATCH_QOS_USER_INTERACTIVE) {
        dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;
    }
    //如果qos等于1触机,那么將值設置為2
    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,
                overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq;
        if (unlikely(!tq)) {
            DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
        }
    }

    /*
     第2步,創(chuàng)建隊列
     */
    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;
    //DQF_MUTABLE             = 0x00400000,
    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 {
        //這個是串行 生成名為OS_dispatch_queue_serial_class的類
        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;
        }
    }

    //開辟內(nèi)存 - 生成響應的對象 queue
    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s));
    /*
     #define DISPATCH_QUEUE_WIDTH_FULL          0x1000ull 也就是4096
     #define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1) 0xfff 也就是4095
     #define DISPATCH_QUEUE_WIDTH_MAX  (DISPATCH_QUEUE_WIDTH_FULL - 2) 0xffe 也就是4094
     */
    //構造方法玷或,初始化隊列屬性
    _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;
}

上面的源碼多數(shù)地方已經(jīng)給了注釋儡首,我們來捋一捋步驟:

1. 通過傳入的參數(shù) 配置隊列屬性

dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

我們來看看dispatch_queue_attr_info_t是個什么結構

typedef uint32_t dispatch_qos_t;
typedef uint32_t dispatch_priority_t;
typedef struct dispatch_queue_attr_info_s {
   //服務質(zhì)量,優(yōu)先級
    dispatch_qos_t dqai_qos : 8;
    int      dqai_relpri : 8;
    //枚舉類型偏友,有三個值蔬胯,分別為不確定、可以復用位他、不可以復用
    uint16_t dqai_overcommit:2;
    uint16_t dqai_autorelease_frequency:2;
    //1為并行氛濒,0位串行
    uint16_t dqai_concurrent:1;
    uint16_t dqai_inactive:1;
} dispatch_queue_attr_info_t;

這是一個很明顯的位域結構,其他屬性我們先不管鹅髓,我們第一眼可以看到dqai_concurrent屬性舞竿,占一位,取值在0和1之間窿冯,我們可以初步推斷dqai_concurrent是用來標識隊列是串行還是并行骗奖。

接下來我們繼續(xù)查看_dispatch_queue_attr_to_info函數(shù)的實現(xiàn)

dispatch_queue_attr_info_t
_dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa)
{
    dispatch_queue_attr_info_t dqai = { };

    //串行隊列直接返回空
    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);

    //設置dispatch_queue_attr_info_t的各個屬性值
    
    //對2進行取余
    dqai.dqai_inactive = (idx % DISPATCH_QUEUE_ATTR_INACTIVE_COUNT);
    //除以2,下降一位,接下來就好設置dqai_concurrent的值了
    idx /= DISPATCH_QUEUE_ATTR_INACTIVE_COUNT;
    
    //對2進行取余执桌,然后再取反鄙皇,得到1
    dqai.dqai_concurrent = !(idx % DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT);
    idx /= DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT;

    //對16取余
    dqai.dqai_relpri = -(int)(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;
}

這個函數(shù)里面具體操作如下:

  • 如果是串行隊列,直接返回空
  • 如果是并行隊列鼻吮,則進行位操作育苟,逐個設置位域內(nèi)部值

設置好隊列屬性初始值之后较鼓,我們接著來規(guī)范化各個參數(shù)

    /*
     規(guī)范化各個參數(shù)椎木,如(qos, overcommit, tq)
     */
    dispatch_qos_t qos = dqai.dqai_qos;
#if !HAVE_PTHREAD_WORKQUEUE_QOS
    //如果qos等于6,那么將值設置為5
    if (qos == DISPATCH_QOS_USER_INTERACTIVE) {
        dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;
    }
    //如果qos等于1博烂,那么將值設置為2
    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,
                overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq;
        if (unlikely(!tq)) {
            DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
        }
    }

這個里面香椎,先設置qos的值,而系統(tǒng)定義了多個宏來表示qos的值禽篱,我們可以看到實際的qos取值在0-6之間畜伐,0表示未確定,還有一個15表示飽和的躺率,而且從這些宏定義的英語取名和我們之前的打印結果可以看出玛界,qos就是關于優(yōu)先級的值,而且還可以看出悼吱,高優(yōu)先級的qos為5慎框,低優(yōu)先級的qos為3,默認優(yōu)先級的qos為4后添,而后臺優(yōu)先級的qos為2笨枯,所以我們可以假定推斷:后臺優(yōu)先級的優(yōu)先級比低優(yōu)先級還低

typedef uint32_t dispatch_qos_t;

#define DISPATCH_QOS_UNSPECIFIED        ((dispatch_qos_t)0)
#define DISPATCH_QOS_MAINTENANCE        ((dispatch_qos_t)1)
#define DISPATCH_QOS_BACKGROUND         ((dispatch_qos_t)2)
#define DISPATCH_QOS_UTILITY            ((dispatch_qos_t)3)
#define DISPATCH_QOS_DEFAULT            ((dispatch_qos_t)4)
#define DISPATCH_QOS_USER_INITIATED     ((dispatch_qos_t)5)
#define DISPATCH_QOS_USER_INTERACTIVE   ((dispatch_qos_t)6)
#define DISPATCH_QOS_MIN                DISPATCH_QOS_MAINTENANCE
#define DISPATCH_QOS_MAX                DISPATCH_QOS_USER_INTERACTIVE
#define DISPATCH_QOS_SATURATED          ((dispatch_qos_t)15)

然后設置overcommit的值,從下面我們可以看到overcommit是一個枚舉

typedef enum {
   //不明確
    _dispatch_queue_attr_overcommit_unspecified = 0,
    //可以復用
    _dispatch_queue_attr_overcommit_enabled,
    //不可以復用
    _dispatch_queue_attr_overcommit_disabled,
} _dispatch_queue_attr_overcommit_t;

再接下來是設置tq的值遇西,由于調(diào)用這個函數(shù)我們傳入的tq為NULL馅精,所以會進入這段代碼

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

這段代碼調(diào)用了_dispatch_get_root_queue函數(shù),傳入兩個參數(shù)粱檀,第一個參數(shù)傳入的值洲敢,如果qos的值不為0,則傳入qos的值茄蚯,否則傳入默認值4压彭;第二個參數(shù)傳入的0或者1,如果可復用則傳入的為1第队,否則傳入0

DISPATCH_ALWAYS_INLINE DISPATCH_CONST
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");
    }
    return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}

我們關注返回值&_dispatch_root_queues[2 * (qos - 1) + overcommit];哮塞,這句的意思是從一個叫_dispatch_root_queues的數(shù)組中取出值,賦值給tq凳谦。
我們來看看這個數(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,
    ),
};

我們可以看出這個數(shù)組一共定義了12個成員忆畅,取得它們其中某一個,然后取地址,就是我們的tq的值家凯。而下標2 * (qos - 1) + overcommit就是根據(jù)我們傳入的值計算得來缓醋,按照默認傳入的default來看,就是2*3+0/1绊诲,也就是6或者7送粱,我們可以看到下標為6的隊列名為"com.apple.root.default-qos",和我們在外面打印出來的默認優(yōu)先級的全局隊列一致
從這里我們大致可以猜想:
默認創(chuàng)建了12個靜態(tài)隊列掂之,包含了全局隊列抗俄,我們獲取全局隊列的時候,是直接在數(shù)組中讀取的

2. 創(chuàng)建隊列

    /*
     第2步世舰,創(chuàng)建隊列
     */
    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;
    //DQF_MUTABLE             = 0x00400000,
    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 {
        //這個是串行 生成名為OS_dispatch_queue_serial_class的類
        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;
        }
    }

    //開辟內(nèi)存 - 生成響應的對象 queue
    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s));
    /*
     #define DISPATCH_QUEUE_WIDTH_FULL          0x1000ull 也就是4096
     #define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1) 0xfff 也就是4095
     #define DISPATCH_QUEUE_WIDTH_MAX  (DISPATCH_QUEUE_WIDTH_FULL - 2) 0xffe 也就是4094
     */
    //構造方法动雹,初始化隊列屬性
    _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__);

一開始是設置并行/串行的隊列類名,使用DISPATCH_VTABLE來設置跟压,我們可以看出

  • 串行隊列類名以OS_dispatch_queue_serial開頭
  • 并行隊列類名以OS_dispatch_queue_concurrent開頭

這和我們一開始打印出來的類名一致

#define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)
#define DISPATCH_OBJC_CLASS(name)   (&DISPATCH_CLASS_SYMBOL(name))
#define DISPATCH_CLASS_SYMBOL(name) OS_dispatch_##name##_class

接下來是設置隊列名label

if (label) {
        const char *tmp = _dispatch_strdup_if_mutable(label);
        if (tmp != label) {
            dqf |= DQF_LABEL_NEEDS_FREE;
            label = tmp;
        }
}

接著根據(jù)設置好的類名胰蝠,分配內(nèi)存,生成queue,我們也可以看到dispatch_lane_t結構體的結構震蒋,這一步我們暫不做深究茸塞,以后研究更多了來補上

//開辟內(nèi)存 - 生成響應的對象 queue
    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s));
#define DISPATCH_LANE_CLASS_HEADER(x) \
    struct dispatch_queue_s _as_dq[0]; \
    DISPATCH_QUEUE_CLASS_HEADER(x, \
            struct dispatch_object_s *volatile dq_items_tail); \
    dispatch_unfair_lock_s dq_sidelock; \
    struct dispatch_object_s *volatile dq_items_head; \
    uint32_t dq_side_suspend_cnt

typedef struct dispatch_lane_s {
    DISPATCH_LANE_CLASS_HEADER(lane);
    /* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN *dispatch_lane_t;
#define DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__) \
    _DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__); \
    /* LP64 global queue cacheline boundary */ \
    unsigned long dq_serialnum; \
    const char *dq_label; \
    DISPATCH_UNION_LE(uint32_t volatile dq_atomic_flags, \
        const uint16_t dq_width, \
        const uint16_t __dq_opaque2 \
    ); \
    dispatch_priority_t dq_priority; \
    union { \
        struct dispatch_queue_specific_head_s *dq_specific_head; \
        struct dispatch_source_refs_s *ds_refs; \
        struct dispatch_timer_source_refs_s *ds_timer_refs; \
        struct dispatch_mach_recv_refs_s *dm_recv_refs; \
        struct dispatch_channel_callbacks_s const *dch_callbacks; \
    }; \
    int volatile dq_sref_cnt

struct dispatch_queue_s {
    DISPATCH_QUEUE_CLASS_HEADER(queue, void *__dq_opaque1);
    /* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN;

然后我們調(diào)用構造方法,對隊列進行初始化

    /*
     #define DISPATCH_QUEUE_WIDTH_FULL          0x1000ull 也就是4096
     #define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1) 0xfff 也就是4095
     #define DISPATCH_QUEUE_WIDTH_MAX  (DISPATCH_QUEUE_WIDTH_FULL - 2) 0xffe 也就是4094
     */
    //構造方法查剖,初始化隊列屬性
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));

從這段代碼我們可以看出钾虐,我們自己創(chuàng)建的串行隊列的width為1,自己創(chuàng)建的并行隊列width為0xffe,也就是(0x1000-0x2),這也和我們之前打印出來的一致

接下是設置隊列標簽(名字)梗搅,設置隊列優(yōu)先級等禾唁,然后調(diào)用_dispatch_retain(tq);將隊列的引用計數(shù)加1

_dispatch_retain(tq);

最后調(diào)用_dispatch_trace_queue_create(dq)._dq來返回創(chuàng)建的隊列

我們看到_dispatch_trace_queue_create調(diào)用了_dispatch_introspection_queue_create,然后_dispatch_introspection_queue_create又調(diào)用了_dispatch_introspection_queue_create最后return upcast(dq)._dqu;dq強轉(zhuǎn)為根父類dispatch_object_t返回其_dqu值无切,這就是我們創(chuàng)建的queue

DISPATCH_ALWAYS_INLINE
static inline dispatch_queue_class_t
_dispatch_trace_queue_create(dispatch_queue_class_t dqu)
{
    _dispatch_only_if_ktrace_enabled({
        uint64_t dq_label[4] = {0}; // So that we get the right null termination
        dispatch_queue_t dq = dqu._dq;
        strncpy((char *)dq_label, (char *)dq->dq_label ?: "", sizeof(dq_label));

        _dispatch_ktrace2(DISPATCH_QOS_TRACE_queue_creation_start,
                dq->dq_serialnum,
                _dispatch_priority_to_pp_prefer_fallback(dq->dq_priority));

        _dispatch_ktrace4(DISPATCH_QOS_TRACE_queue_creation_end,
                        dq_label[0], dq_label[1], dq_label[2], dq_label[3]);
    });

    return _dispatch_introspection_queue_create(dqu);
}

再看_dispatch_introspection_queue_create函數(shù)的實現(xiàn)

dispatch_queue_class_t
_dispatch_introspection_queue_create(dispatch_queue_t dq)
{
    dispatch_queue_introspection_context_t dqic;
    size_t sz = sizeof(struct dispatch_queue_introspection_context_s);

    if (!_dispatch_introspection.debug_queue_inversions) {
        sz = offsetof(struct dispatch_queue_introspection_context_s,
                __dqic_no_queue_inversion);
    }
    dqic = _dispatch_calloc(1, sz);
    dqic->dqic_queue._dq = dq;
    if (_dispatch_introspection.debug_queue_inversions) {
        LIST_INIT(&dqic->dqic_order_top_head);
        LIST_INIT(&dqic->dqic_order_bottom_head);
    }
    dq->do_finalizer = dqic;

    _dispatch_unfair_lock_lock(&_dispatch_introspection.queues_lock);
    LIST_INSERT_HEAD(&_dispatch_introspection.queues, dqic, dqic_list);
    _dispatch_unfair_lock_unlock(&_dispatch_introspection.queues_lock);

    DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_create, dq);
    if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_create)) {
        _dispatch_introspection_queue_create_hook(dq);
    }
    //強轉(zhuǎn)為dispatch_object_t類型 然后返回
    return upcast(dq)._dqu;
}

最后我們來看一看dispatch_object_tdispatch_queue_class_t的結構

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;
typedef union {
    struct dispatch_queue_s *_dq;
    struct dispatch_workloop_s *_dwl;
    struct dispatch_lane_s *_dl;
    struct dispatch_queue_static_s *_dsq;
    struct dispatch_queue_global_s *_dgq;
    struct dispatch_queue_pthread_root_s *_dpq;
    struct dispatch_source_s *_ds;
    struct dispatch_channel_s *_dch;
    struct dispatch_mach_s *_dm;
    dispatch_lane_class_t _dlu;
#ifdef __OBJC__
    id<OS_dispatch_queue> _objc_dq;
#endif
} dispatch_queue_class_t DISPATCH_TRANSPARENT_UNION;

三荡短、主隊列

我們從dispatch_get_main_queue()入手

DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_CONST DISPATCH_NOTHROW
dispatch_queue_main_t
dispatch_get_main_queue(void)
{
    return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);
}

源碼中調(diào)用了DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);,我們先看看DISPATCH_GLOBAL_OBJECT是什么東西

#define DISPATCH_GLOBAL_OBJECT(type, object) ((OS_OBJECT_BRIDGE type)&(object))

然后我們再看看兩個參數(shù),我們先看看dispatch_queue_main_t

DISPATCH_DECL_SUBCLASS(dispatch_queue_main, dispatch_queue_serial);

#define DISPATCH_DECL_SUBCLASS(name, base) OS_OBJECT_DECL_SUBCLASS(name, base)

#define OS_OBJECT_DECL_SUBCLASS(name, super) \
        OS_OBJECT_DECL_IMPL(name, <OS_OBJECT_CLASS(super)>)
        
#define OS_OBJECT_DECL_IMPL(name, ...) \
        OS_OBJECT_DECL_PROTOCOL(name, __VA_ARGS__) \
        typedef NSObject<OS_OBJECT_CLASS(name)> \
                * OS_OBJC_INDEPENDENT_CLASS name##_t

從上面的源碼我們可以看出哆键,主隊列是串行隊列的子類掘托,通過對父類方法的重寫,得到了dispatch_queue_main_t

我們再看第二個參數(shù)_dispatch_main_q籍嘹,找到

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

這個結構體設置了主線程的各個屬性闪盔,從結構體中我們可以看到dq_label = "com.apple.main-thread",這表示是我們的主線程辱士,這也和我們的打印一致

四泪掀、全局隊列

我們也從dispatch_get_global_queue函數(shù)出發(fā)

dispatch_queue_global_t
dispatch_get_global_queue(long priority, unsigned long flags)
{
    dispatch_assert(countof(_dispatch_root_queues) ==
            DISPATCH_ROOT_QUEUE_COUNT);

    if (flags & ~(unsigned long)DISPATCH_QUEUE_OVERCOMMIT) {
        return DISPATCH_BAD_INPUT;
    }
    dispatch_qos_t qos = _dispatch_qos_from_queue_priority(priority);
#if !HAVE_PTHREAD_WORKQUEUE_QOS
    if (qos == QOS_CLASS_MAINTENANCE) {
        qos = DISPATCH_QOS_BACKGROUND;
    } else if (qos == QOS_CLASS_USER_INTERACTIVE) {
        qos = DISPATCH_QOS_USER_INITIATED;
    }
#endif
    if (qos == DISPATCH_QOS_UNSPECIFIED) {
        return DISPATCH_BAD_INPUT;
    }
    return _dispatch_get_root_queue(qos, flags & DISPATCH_QUEUE_OVERCOMMIT);
}

這個函數(shù)最后調(diào)用_dispatch_get_root_queue函數(shù)

DISPATCH_ALWAYS_INLINE DISPATCH_CONST
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");
    }
    return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}

在第二段創(chuàng)建隊列中我們可以看到,_dispatch_get_root_queue函數(shù)的返回值是從_dispatch_root_queues數(shù)組中取出颂碘,我們再次看看這個數(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,
    ),
};

我們可以看到异赫,根據(jù)我們傳入的不同的優(yōu)先級,全局隊列從_dispatch_root_queues數(shù)組中取出預先寫好的隊列來返回。

而從.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL)這句中可以看到塔拳,全局隊列的寬度為0xfff鼠证,寬度大于1,說明全局隊列是一個并發(fā)隊列靠抑,由前面可知量九,我們自己創(chuàng)建的并行隊列寬度為0xffe,這就是全局隊列和自創(chuàng)的并發(fā)隊列的區(qū)別

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市颂碧,隨后出現(xiàn)的幾起案子荠列,更是在濱河造成了極大的恐慌,老刑警劉巖稚伍,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弯予,死亡現(xiàn)場離奇詭異,居然都是意外死亡个曙,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門受楼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來垦搬,“玉大人,你說我怎么就攤上這事艳汽『锓。” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵河狐,是天一觀的道長米绕。 經(jīng)常有香客問我,道長馋艺,這世上最難降的妖魔是什么栅干? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮捐祠,結果婚禮上碱鳞,老公的妹妹穿的比我還像新娘。我一直安慰自己踱蛀,他們只是感情好窿给,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著率拒,像睡著了一般崩泡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猬膨,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天角撞,我揣著相機與錄音,去河邊找鬼。 笑死靴寂,一個胖子當著我的面吹牛磷蜀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播百炬,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼褐隆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了剖踊?” 一聲冷哼從身側(cè)響起庶弃,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎德澈,沒想到半個月后歇攻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡梆造,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年缴守,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镇辉。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡屡穗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出忽肛,到底是詐尸還是另有隱情村砂,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布屹逛,位于F島的核電站础废,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏罕模。R本人自食惡果不足惜评腺,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望手销。 院中可真熱鬧歇僧,春花似錦、人聲如沸锋拖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽兽埃。三九已至侥钳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間柄错,已是汗流浹背舷夺。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工苦酱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人给猾。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓疫萤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親敢伸。 傳聞我的和親對象是個殘疾皇子扯饶,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容