iOS底層原理探究(2)

NSObject 為什么沒有進入源碼斷點?


NSObject的創(chuàng)建已經(jīng)在系統(tǒng)級運行中初始化完了,執(zhí)行了objc_alloc方法 ,第一次初始化的類是NSArray(調(diào)試可運行的源碼得知),NSArray的父類就是NSObject 必定會初始化,所以不會進入斷點。

alloc方法為何會執(zhí)行兩次

callAlloc走兩次的原因是:第一次系統(tǒng)會執(zhí)行objc_alloc,調(diào)用callAlloc方法,執(zhí)行objc_msgSend 執(zhí)行alloc方法,繼續(xù)調(diào)用callAlloc方法,生成實例。

我們手寫執(zhí)行LGPerson alloc 是不會調(diào)用了,因為系統(tǒng)已經(jīng)調(diào)用了alloc,存在緩存中了。

系統(tǒng)執(zhí)行:LLVM(蘋果系統(tǒng)層級)alloc特殊修飾,我們?nèi)魏蔚闹盗慷紩辉谙到y(tǒng)級別進行sel處理,從LLVM源碼可以深入。

LLVM源碼探究:

1.LLVM源碼中alloc的分支如下

2.EmitObjCAlloc會轉(zhuǎn)化為objc_alloc

3.在objc源碼中,callAlloc方法中不滿足邏輯,會執(zhí)行alloc消息發(fā)送

4.LLVM消息發(fā)送的源碼

內(nèi)存對齊:

1.在控制臺打?。?/p>

po 打印對象

x? 打印內(nèi)存地址 ,dubug下-view memory

x/4gx? 打印16進制四個地址,

首地址為內(nèi)存地址


控制臺打印,直接取值。

控制臺打?。好疤柷埃寒斍皟?nèi)存的的地址,冒號后:當前存儲的值

0x0000000000000:當前屬性沒有被賦值

對象在底層編譯成結(jié)構(gòu)體,內(nèi)存對齊來自于結(jié)構(gòu)體


內(nèi)存地址不是完全對齊,字符串是8字節(jié),整形與字符類型會優(yōu)化在一起,98 97代表的是ASCII碼對應(yīng)的值。

結(jié)構(gòu)體中的內(nèi)存對齊:


內(nèi)存對齊原則

結(jié)構(gòu)體內(nèi)從0開始排列,開辟過程中結(jié)構(gòu)體中的子元素一定要是資深數(shù)據(jù)類型的整數(shù)倍,如果不夠,則需要空出,繼續(xù)向前排列,最終計算后的字節(jié)一定是以結(jié)構(gòu)體中最大的數(shù)據(jù)類型整數(shù)倍去開辟。

舉例:


1.double類型字節(jié)為8,開辟內(nèi)存空間0-7 將a放入內(nèi)存

2.char類型字節(jié)為1 ,從8開始排列, 由于8是1的整數(shù)倍,可以將b放入內(nèi)存中(起始位置必須是當前類型的整數(shù)倍)

3.int類型字節(jié)為4,從9開始排列,9不是4的整數(shù)倍,將空間空出,繼續(xù)排列,10,11也不是4的整數(shù)倍繼續(xù)空出。12剛好是4的整數(shù)倍,因此從12開始排列:12,13,14,15.

4.short類型字節(jié)為2,從16開始排列,16是2的整數(shù)倍,從16開始排列:16,17

5.最終結(jié)構(gòu)體占用17字節(jié),根據(jù)內(nèi)存補齊原則,最終開辟內(nèi)存區(qū)域一定是最大數(shù)據(jù)類型 double的整數(shù)倍(即8的整數(shù)倍),因此為24

內(nèi)存分配示意圖:

結(jié)構(gòu)體嵌套的內(nèi)存對齊:


已知LGstruct1 結(jié)構(gòu)體為18字節(jié),LGstruct1 結(jié)構(gòu)體中最大元素為8字節(jié),根據(jù)內(nèi)存對齊原則必須為8的整數(shù)倍,因此為24 ,LGstruct2中其他元素共占用16字節(jié)(不算e),因此 16+24 = 40

如果LG2結(jié)構(gòu)體加上int類型 e,e占用4字節(jié)(16-19),最小8的倍數(shù)為24 ,因此24 + 24 = 48字節(jié)。

LGstruct1共需要24字節(jié),為何不是24的倍數(shù),因為結(jié)構(gòu)體嵌套中,依然會遵循結(jié)構(gòu)體中最大字節(jié)元素的倍數(shù)原則。

內(nèi)存對齊優(yōu)勢:方便讀取,不會存在讀取混亂 ,安全,防止讀到別的變量區(qū)域。

蘋果進行屬性重排,優(yōu)化內(nèi)存。

分配內(nèi)存大小與實際數(shù)據(jù)內(nèi)容大小的打?。?/p>

第一個地址打印的是person的指針(isa)占用8字節(jié)

第二個地址打印的是person對象真正需要的內(nèi)存

第三個地址打印的是系統(tǒng)實際分配了多少內(nèi)存。

閱讀源碼的能力(通過calloc方法找到核心代碼):

從iOS底層原理探究1得到alloc底層生成方法需要三步

1.告知系統(tǒng)申請多少內(nèi)存:instanceSize

2.開辟內(nèi)存拿回指針 :calloc

3.類和指針綁定:initInstanceIsa

今天我們就探究一下calloc方法:(此方法在-libMalloc源碼下載

此處只涉及如何快速從源碼中檢索核心代碼,源碼閱讀會放在最后。

此方法返回retval,因此給retval的賦值是核心代碼。

zone->calloc?

zone->calloc:**->**在C語言中稱為間接引用運算符,是二目運算符,優(yōu)先級同成員運算符“.”

zone是一個_malloc_zone_t 類型的結(jié)構(gòu)體的指針,calloc是結(jié)構(gòu)體中的成員(方法也可以是結(jié)構(gòu)體中的成員),此寫法等同于?(*zone).calloc。

從zone -> calloc如何繼續(xù)深入?

在控制臺(lidb)打印我們要深入的方法:default_zone_calloc,給結(jié)構(gòu)體開辟內(nèi)存的方法

按住control鍵,選擇進入斷點,也可以進入此方法。

從此方法?default_zone_calloc 全局搜索繼續(xù)深入

全局搜索繼續(xù)深入nano_calloc方法

核心代碼:在_nano_malloc_check_clear繼續(xù)深入(如何保持16字節(jié)內(nèi)存對齊的算法就在這了)

解讀segregated_size_to_fit方法(16進制對齊算法):

宏定義#define NANO_REGIME_QUANTA_SIZE (1<< SHIFT_NANO_QUANTUM):1向左移動4位后邊補0。

二進制演示:0000 1000? 轉(zhuǎn)為10進制位2的4次方,得到注釋結(jié)果16.

0001 0001:為17的為二進制轉(zhuǎn)換

0000 0001:右移4位,尾部抹零

00001 0000:左移4位 ,抹零后得到16.

標紅區(qū)域為帶入實際值的 方法演算過程。

下面是對calloc方法的核心源碼以及變量的注釋和理解:

calloc方法
2

總結(jié):重點是當我們我的斷點無法深入的時候,通過LLDB控制臺打印 指針-也會展示出我們想要方法,在通過源碼全局搜索

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

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