在上一篇文章中以[LBHPerson alloc]為例對(duì)+alloc方法進(jìn)行了源碼分析,本文作為補(bǔ)充去探索作為根類的NSObject的[NSObject alloc]流程與[LBHPerson alloc]流程是否有區(qū)別。
源碼分析
沿用上一篇文章的objc4-781源碼,新增一個(gè)NSObject的實(shí)例并打上斷點(diǎn)

根據(jù)上一篇文章分析的alloc流程

等程序執(zhí)行到main函數(shù)的NSObject *obj = [NSObject alloc]的斷點(diǎn)時(shí), 將上面流程中所有的方法打上斷點(diǎn)繼續(xù)運(yùn)行,會(huì)發(fā)現(xiàn)執(zhí)行完callAlloc方法后直接執(zhí)行_objc_rootAllocWithZone方法,即直接從第二步到第六步,并沒有走+ alloc流程,可以得出[NSObject alloc]的流程圖

問題
1、為什么明明調(diào)用的是+ alloc方法首先執(zhí)行的卻是 objc_alloc方法?
2、為什么[LBHPerson alloc]的流程中callAlloc調(diào)用兩次?
3、為什么[NSObject alloc]流程沒有執(zhí)行+ alloc方法?
分析
問題1、為什么明明調(diào)用的是+ alloc方法首先執(zhí)行的卻是 objc_alloc方法?
準(zhǔn)備工作
action
step1:在vscode工具中打開llvm源碼,搜索omf_alloc:找到tryGenerateSpecializedMessageSend,表示嘗試生成特殊消息發(fā)送

當(dāng)接收到alloc名稱的selector時(shí),調(diào)用EmitObjCAlloc函數(shù)。
step2:跳轉(zhuǎn)至EmitObjCAlloc的定義可以看到alloc 的處理是調(diào)用了 objc_alloc

由此可以得出:llvm在編譯啟動(dòng)時(shí),alloc方法會(huì)被轉(zhuǎn)換為objc_alloc方法
問題2、為什么[NSObject alloc]流程沒有執(zhí)行+ alloc方法?
action
step1:在objc4-781源碼中,打上以后幾個(gè)斷點(diǎn),然后運(yùn)行

會(huì)發(fā)現(xiàn)斷點(diǎn)并沒有進(jìn)入到main函數(shù)里,而是進(jìn)入objc_alloc,cls為NSArray,這意味系統(tǒng)在初始化做了很多工作,我們來看下這個(gè)[NSArray alloc]的調(diào)用情況

step2:在callAlloc方法根據(jù)斷點(diǎn)一步步往下走,會(huì)進(jìn)入objc_msgSend消息發(fā)送,

step3: +alloc方法
+ (id)alloc {
return _objc_rootAlloc(self);
}
step4:_objc_rootAlloc方法
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
step5:callAlloc方法,同step2調(diào)用的是同一個(gè)方法,我們跟著斷點(diǎn)一步步走,最后通過objc_msgSend消息發(fā)送調(diào)用系統(tǒng)的allocWithZone方法。

看一下它的堆棧調(diào)用情況

在step2中objc_msgSend發(fā)送alloc消息時(shí),當(dāng)前的cls為NSArray,類中沒有自定義的+ alloc方法,所以會(huì)去它的父類也就是NSObject中查找,堆棧調(diào)用顯示的是調(diào)用的NSObject的+alloc方法,還沒到main函數(shù)中手動(dòng)調(diào)用[NSObject alloc]時(shí)系統(tǒng)就已經(jīng)處理好了。
step6:先暫時(shí)關(guān)掉非main函數(shù)中的斷點(diǎn),等程序執(zhí)行到main函數(shù)斷點(diǎn),再開啟這些斷點(diǎn),著重看一下callAlloc函數(shù)

由此可以得出:系統(tǒng)初始化時(shí)NSObject已經(jīng)完成+alloc操作
問題3、為什么[LBHPerson alloc]的流程中callAlloc調(diào)用兩次?
action
step1:回到llvm源碼,經(jīng)過前面的分析已經(jīng)知道llvm會(huì)將+alloc轉(zhuǎn)發(fā)成objc_alloc,找到實(shí)現(xiàn)它的方法

step2:搜索tryGenerateSpecializedMessageSend,看它的調(diào)用情況

- 在
GeneratePossiblySpecializedMessageSend中tryGenerateSpecializedMessageSend被調(diào)用,說明runtime消息處理時(shí)必先調(diào)用這個(gè)函數(shù) - 如果滿足
if條件,則調(diào)用特殊消息發(fā)送,即將alloc轉(zhuǎn)發(fā)成objc_alloc - 如果不滿足,則調(diào)用普通消息發(fā)送
由此可以得出:第一次調(diào)用+alloc方法會(huì)調(diào)用特殊消息處理,即將alloc轉(zhuǎn)發(fā)成objc_alloc;第二次會(huì)調(diào)用普通消息處理
總結(jié)
- NSObject的創(chuàng)建由系統(tǒng)處理
- 自定義類alloc創(chuàng)建時(shí),會(huì)執(zhí)行兩次,第一次判斷條件時(shí)執(zhí)行,第二次常規(guī)方法執(zhí)行。