iOS底層原理探索篇 主要是圍繞底層進行
源碼分析
-LLDB調試
-源碼斷點
-彙編調試
,讓本身之後回顧複習的😀😀設計模式目錄以下:bash
咱們先來看下面這段代碼:ui
TCJPerson *p1 = [TCJPerson alloc];
TCJPerson *p2 = [p1 init];
TCJPerson *p3 = [p1 init];
複製代碼
看完代碼,有一個疑問就是:p一、p二、p3有什麼聯繫呢? 爲了弄清此問題,那就看運行結果唄: spa
alloc
和
init
到底作了什麼呢? 咱們先來看看
alloc
的實現:下面介紹三種方式來查看他的實現.(查看過程需用真機查看,由於模擬器查找的是x86_64環境和arm64是不同的)
objc_alloc
在
libobjc.A.dylib
這個動態庫裏面.
第一步點左下角的**+號按鈕以後,在點擊Symbolic Breakpoint**設計
第二部添加alloc符號斷點:3d
以後過掉斷點以後會顯示以下:
如何操做以下圖箭頭所示:
以後會顯示下圖所示:
這時咱們能夠看到在22行有objc_alloc
,那在此處打下斷點按住control鍵和圖上鍵頭所指的鍵結果所下:
以後咱們繼續以前的操做(按住control鍵和箭頭所指的鍵)結果以下:
經過這三種方式咱們能夠知道objc_alloc
在libobjc.A.dylib
這個動態庫裏面,那接下來咱們來經過alloc
的源碼來分析.在這以前,咱們用寄存器來讀取一下:那麼什麼是寄存器呢? 寄存器就是應該存儲一些指針的一些東西,由於彙編它就是利用寄存器,用的妥妥的.過掉第一個斷點(37行斷點),來到alloc
斷點以下:
x0~x7
用於程序調用的參數傳遞,
x0
是第一個參數的傳遞者也是返回的時候返回值的存儲地方.所以咱們通常讀x0就能夠了.以後過掉
alloc
斷點來到
_objc_rootAlloc
斷點:
objc_msgSend
打上斷點,來到斷點處:
打開可編譯的objc756.2源碼,經過前面的探究咱們能夠看到,在調用alloc以前還調用了objc_alloc,咱們打下斷點一步一步去看,圖以下:
咱們先來到這個斷點之處,而後全局搜索objc_alloc
,以下圖打上斷點,以後咱們過掉上圖的斷點,回來到下面的斷點之處,這時咱們看到allocWithZone
返回的是false
.在下圖中我作了詳細的解釋.
那麼爲何objc_alloc
這個流程只會走一次呢?請看下圖
從上圖中能夠看到符號綁定的操做是在fixupMessageRef
這個方法裏面實現的.而fixupMessageRef
的調用又是在_read_images裏面調用的.也就是dyld讀取咱們的鏡像文件的時候.然而,在咱們讀取鏡像文件的時候,系統會判斷是否須要FIXUP,若是須要的話,咱們就會調用fixupMessageRef
,而後在fixupMessageRef
內部判斷當前的消息sel是不是SEL_alloc,若是是的話就替換其IMP爲objc_alloc.其中FIXUP只會走一次,也就是說objc_alloc只會走一次. 以後會繼續走callAlloc方法:在這個方法中以下圖所示allocWithZone返回false,以後在走alloc方法.
objc_alloc
只會走一次:
先說說爲何會走objc_alloc
?由於在LLVM
的底層會調用CGF.EmitCallOrInvoke
在說說objc_alloc
只會走一次:
call
-objc_alloc
->none
沒有返回對象;經過LLVM
源碼能夠看到:
none
時,return CGF.EmitObjCAlloc(Receiver, CGF.ConvertType(ResultType))
複製代碼
也返回爲none
,那麼就會進行下面的條件判斷
if (Optional<llvm::Value *> SpecializedResult =
tryGenerateSpecializedMessageSend(CGF, ResultType, Receiver, Args,Sel, Method, isClassMessage))
複製代碼
而此時並無進入
return RValue::get(SpecializedResult.getValue())
複製代碼
可是此時調用了objc_alloc
方法,以後會繼續走
return GenerateMessageSend(CGF, Return, ResultType, Sel, Receiver, Args, OID, Method)
複製代碼
調用alloc方法.
此方法內部有一系列的判斷條件,其中因爲方法canAllocFast()的內部調用了bits.canAllocFast(),其返回值爲固定值false,因此能夠肯定以後建立對象會走class_createInstance方法
進入class_createInstance
方法,其內部調用了_class_createInstanceFromZone
方法,並在其中進行size計算,內存申請,以及isa初始化:
咱們先來看看對象size
的計算,經過方法cls->instanceSize(extraBytes),計算出size,其中64位系統下,對象大小採用8字節對齊,可是實際申請的內存最低爲16字節,也就是說系統分配內存按照16字節對齊分配
obj = (id)calloc(1, size)
obj->initInstanceIsa(cls, hasCxxDtor)
alloc
的做用 咱們來對這兩個核心內容分析一下: 建立指針,申請內存
calloc
函數建立了一個指針,這個指針是怎麼建立的,這個源碼在:libmalloc
.(具體的分析在OC對象原理(二) 內存對齊探索&malloc源碼分析文中有寫) 最後根據不一樣的條件,使用calloc
或者malloc_zone_calloc
進行內存申請,而且初始化isa
指針,至此size
大小的對象obj
已經申請完成,而且返回.init
方法什麼事情都沒有作。那爲何
init
會什麼都不作呢? 其實這是一種設計模式,本身思考一下,平常開發過程當中,咱們會在什麼狀況下,進行
init
方法的使用。
—— 重寫 在重寫默認初始化的時候,咱們能夠根據本身的需求,進行各類個性化的設置。 工廠設計,
父類沒有執行,交給子類去實現。
至此,咱們在回到前面的問題?就很好的知道p一、p二、p3他們的內存地址爲何同樣了吧.
進入new
方法:
最後附上alloc流程圖一張