回顧
在上篇博客已經(jīng)介紹了各種隊(duì)列和異步、同步函數(shù)的組合,GCD的隊(duì)列和函數(shù),對(duì)隊(duì)列和任務(wù)的執(zhí)行有了清晰的認(rèn)識(shí), 那么本篇博客將繼續(xù)介紹GCD的隊(duì)列和源碼分析。
1. 主隊(duì)列分析
查看主隊(duì)列的api如下圖:
- 主隊(duì)列是一個(gè)特殊的串行隊(duì)列
- 主隊(duì)列在調(diào)用
main()函數(shù)之前自動(dòng)創(chuàng)建的。 - 主隊(duì)列在應(yīng)用程序上下文中用于與主線程和main runloop 交互。
那么斷點(diǎn)在
main函數(shù)處去驗(yàn)證一下
通過(guò)斷點(diǎn),確實(shí)驗(yàn)證了主隊(duì)列是在調(diào)用
main()函數(shù)之前自動(dòng)創(chuàng)建的。
那么我們要看底層源碼,該怎么看啊,首先我們得知道 GCD是屬于哪個(gè)源碼的,才能進(jìn)一步去探索分析。
-
再次通過(guò)斷點(diǎn)尋找,如下圖
尋找 GCD源碼出處
通過(guò)bt打印堆棧信息,可以定位到libdispatch.dylib動(dòng)態(tài)庫(kù),那么就去蘋(píng)果開(kāi)源網(wǎng)站去下載源碼試試。 -
下載libdispatch.dylib源碼,探索
GCD。
libdispatch -
libdispatch源碼
GCD源碼libdispatch
libdispatch源碼比較難受 注釋非常的少
宏定義非常的多
函數(shù)名非常的長(zhǎng)
這是一個(gè)硬骨頭,非常的不好啃,只能硬著頭皮慢慢啃了,難受?。?/p>
- 源碼搜索
dispatch_get_main_queue
dispatch_get_main_queue
dispatch_get_main_queue是通過(guò)DISPATCH_GLOBAL_OBJECT返回的,是一個(gè)宏定義
-
DISPATCH_GLOBAL_OBJECT
DISPATCH_GLOBAL_OBJECT宏定義 - 通過(guò)
_dispatch_main_q參數(shù)搜索
dispatch_get_main_queue(void)
{
return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);
}
- 還可以通過(guò)主隊(duì)列的
dq_label搜索如下:
在源碼中有
dq_serialnum = 1,這是不是意味著可以作為主隊(duì)列就是 串行隊(duì)列的依據(jù)呢? 現(xiàn)在還不得而知,那么去看看串行隊(duì)列底層是怎么實(shí)現(xiàn)的,或許可以找到答案!
- 主隊(duì)列的初始化是在
dispatch_init()方法中?
在這里插入圖片描述 - 在
dispatch_init()中成功找到了主隊(duì)列初始化的地方, - 獲取默認(rèn)隊(duì)列,
- 并將主隊(duì)列地址綁定到當(dāng)前隊(duì)列和主線程中
2. 串行、并發(fā)隊(duì)列分析
串行隊(duì)列和并發(fā)隊(duì)列都通過(guò)dispatch_queue_create創(chuàng)建的,那么去搜索一下
通過(guò)搜索定位到
dispatch_queue_create,在通過(guò)返回的是_dispatch_lane_create_with_target,再繼續(xù)搜索-
代碼比較長(zhǎng),從返回值看,再推導(dǎo)
從返回值推導(dǎo) -
_dispatch_object_alloc申請(qǐng)內(nèi)存空間 -
_dispatch_queue_init構(gòu)造函數(shù)初始化 - 判斷是否為并發(fā)隊(duì)列,如果是,傳入
DISPATCH_QUEUE_WIDTH_MAX,否則傳入1。也就是說(shuō),串行隊(duì)列這里傳入1,如果是并發(fā)隊(duì)列,則傳入DISPATCH_QUEUE_WIDTH_MAX
DISPATCH_QUEUE_WIDTH_MAX - 對(duì)
dq進(jìn)行設(shè)置,如dq_label、dq_priority等
_dispatch_queue_init
- 把前面的
width傳進(jìn)來(lái),賦值dqf |= DQF_WIDTH(width) -
DQF_WIDTH(width),也就是用來(lái)確定隊(duì)列的類型,以此來(lái)區(qū)分串行隊(duì)列和并發(fā)隊(duì)列
DISPATCH_QUEUE_SERIAL_NUMBER_INIT
其他參數(shù)vtable、dqai,分別是什么呢?繼續(xù)探索
在這里插入圖片描述 dqai初始化
在開(kāi)頭有這么一句代碼
// dqai 創(chuàng)建 - dqa傳入的屬性串行還是并行
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
-
_dispatch_queue_attr_to_info
_dispatch_queue_attr_to_info
在這里進(jìn)行初始化了dqai,并判斷dqa的類型,如果是并發(fā)隊(duì)列,則設(shè)置并發(fā)隊(duì)列為true,否則默認(rèn)為串行隊(duì)列。在調(diào)用_dispatch_queue_init對(duì)dq進(jìn)行構(gòu)造時(shí),對(duì)隊(duì)列類型進(jìn)行了區(qū)分,也就是DQF_WIDTH(width)的傳參,串行隊(duì)列width=1,否則為并發(fā)隊(duì)列。 vtable
const void *vtable; // - 設(shè)置類 類是通過(guò)宏定義拼接而成
if (dqai.dqai_concurrent) {
// OS_dispatch_##name##_class
// OS_dispatch_queue_concurrent - 宏定義拼接類類型
vtable = DISPATCH_VTABLE(queue_concurrent);
} else {
vtable = DISPATCH_VTABLE(queue_serial);
}
vtable可以理解為是一個(gè)類,或者說(shuō)構(gòu)造隊(duì)列的模板類,qai來(lái)區(qū)分隊(duì)列的類型,根據(jù)隊(duì)列的類型來(lái)初始化不同的vtable。DISPATCH_VTABLE是一個(gè)宏定義的方法,全局搜索DISPATCH_VTABLE的定義
// DISPATCH_VTABLE定義
#define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)
// vtable symbols - 模板
#define DISPATCH_OBJC_CLASS(name) (&DISPATCH_CLASS_SYMBOL(name))
// 拼接形成類
#define DISPATCH_CLASS_SYMBOL(name) OS_dispatch_##name##_class
DISPATCH_VTABLE函數(shù)的傳參根據(jù)不同的隊(duì)列類型傳參不一致。
并發(fā)隊(duì)列
傳queue_concurrent參數(shù),最終拼接后,隊(duì)列類型對(duì)應(yīng)的類為:OS_dispatch_queue_concurrent
串行隊(duì)列
傳queue_serial參數(shù),最終拼接后,隊(duì)列類型對(duì)應(yīng)的類為:OS_dispatch_queue_serial
所以vtable對(duì)應(yīng)的就是隊(duì)列的類型。通過(guò)拼接完成類的定義,這和我們?cè)趹?yīng)用層使用的隊(duì)列類型是一致的。
3. 全局隊(duì)列分析
進(jìn)入
dispatch_get_global_queue的api
- 創(chuàng)建全局并發(fā)隊(duì)列時(shí)可以傳參數(shù)
- 根據(jù)不同服務(wù)質(zhì)量或者優(yōu)先等級(jí)提供不同的并發(fā)隊(duì)列。
通過(guò)全局隊(duì)列的標(biāo)識(shí) 在源碼里面搜索??
- 系統(tǒng)會(huì)維護(hù)一個(gè)
全局隊(duì)列集合, - 根據(jù)不同的
服務(wù)質(zhì)量或者優(yōu)先等級(jí)提供不同的全局隊(duì)列。 - 我們?cè)陂_(kāi)發(fā)工作中默認(rèn)使用:
dispatch_get_global_queue(0, 0)。
更多內(nèi)容持續(xù)更新
?? 喜歡就點(diǎn)個(gè)贊吧????
?? 覺(jué)得有收獲的,可以來(lái)一波,收藏+關(guān)注,評(píng)論 + 轉(zhuǎn)發(fā),以免你下次找不到我????
??歡迎大家留言交流,批評(píng)指正,互相學(xué)習(xí)??,提升自我??