iOS-底層原理4:NSObject的alloc分析

上一篇文章中以[LBHPerson alloc]為例對(duì)+alloc方法進(jìn)行了源碼分析,本文作為補(bǔ)充去探索作為根類的NSObject[NSObject alloc]流程與[LBHPerson alloc]流程是否有區(qū)別。

源碼分析

沿用上一篇文章的objc4-781源碼,新增一個(gè)NSObject的實(shí)例并打上斷點(diǎn)

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

[LBHPerson 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]的流程圖

[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)備工作

llvm源碼

action

step1:vscode工具中打開llvm源碼,搜索omf_alloc:找到tryGenerateSpecializedMessageSend,表示嘗試生成特殊消息發(fā)送

image

當(dāng)接收到alloc名稱的selector時(shí),調(diào)用EmitObjCAlloc函數(shù)。

step2:跳轉(zhuǎn)至EmitObjCAlloc的定義可以看到alloc 的處理是調(diào)用了 objc_alloc

image

由此可以得出:llvm在編譯啟動(dòng)時(shí),alloc方法會(huì)被轉(zhuǎn)換為objc_alloc方法

問題2、為什么[NSObject alloc]流程沒有執(zhí)行+ alloc方法?

action

step1:objc4-781源碼中,打上以后幾個(gè)斷點(diǎn),然后運(yùn)行

image

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

image

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

image

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方法。

image

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

step2objc_msgSend發(fā)送alloc消息時(shí),當(dāng)前的clsNSArray,類中沒有自定義的+ 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ù)

callAlloc

由此可以得出:系統(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)它的方法

tryGenerateSpecializedMessageSend

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

  • GeneratePossiblySpecializedMessageSendtryGenerateSpecializedMessageSend被調(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í)行。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容