iOS底層原理_02:OC對象原理(中)

第二節(jié)課 OC對象原理(中)

底層LLVM優(yōu)化

上篇文章我們說到,實際代碼查看的流程是 alloc->_objc_rootAlloc->callAlloc->_objc_rootAllocWithZone,但是,我們通過斷點發(fā)現(xiàn)實際流程卻是alloc->objc_alloc->callAlloc->objc_msgSend->alloc->_objc_rootAlloc-callAlloc,這個到底是為啥呢劣领?

原因是蘋果覺得alloc是比較特殊的方法瞪浸,只要是alloc购岗,就先走objc_alloc(類似hook),執(zhí)行了一些底層優(yōu)化氨距、標記精偿,再執(zhí)行alloc方法针贬,具體需要探索LLVM的源碼钓觉,這個我們后續(xù)再進行補充。
LLVM下載地址

所以實際的alloc流程應該為:
alloc->objc_alloc->LLVM底層優(yōu)化坚踩、標記等等->objc_alloc->objc_msgSend->alloc->LLVM判斷標記過->_objc_rootAlloc
這也是為什么callalloc走兩次的原因荡灾,所以我們將之前的流程圖再次補充一下。

alloc主線流程圖補充.png

對象的內(nèi)存的影響因素

上篇文章我們討論了一下字節(jié)對齊瞬铸,字節(jié)對齊的最終又是以內(nèi)存的方式展現(xiàn)批幌,所以我們來探究一下能影響內(nèi)存的因素。
先看下我們上篇文章寫的例子


02-內(nèi)存影響因素.png

正常的class_getInstanceSize,是32嗓节,那我們干掉一些屬性后呢荧缘?發(fā)現(xiàn)有減少


02-屬性變量影響內(nèi)存.png

這證明我們的屬性是對內(nèi)存有影響的,那成員變量應該也是一樣的拦宣,我們添加后發(fā)現(xiàn)截粗,確實影響了內(nèi)存大小。


02-成員變量影響內(nèi)存.png
02-成員變量影響內(nèi)存2.png

那么方法呢鸵隧?添加了一個方法后發(fā)現(xiàn)并沒有變化绸罗,因為方法不占用內(nèi)存,這個我們后續(xù)會再詳細進行講解豆瘫。對于內(nèi)存的理解我們可以先看下面的圖珊蟀。


02-內(nèi)存畫圖舉例.png

Person通過alloc開辟了一塊堆的空間,外部通過對象的地址(棧里)來進行指向外驱。這個內(nèi)存空間里的就是Person里面的各項成員變量以及isa育灸。

02-x:8gx輸出.png

我們新增幾條屬性后,通過x/8gx輸出看到昵宇,左邊0x60000336b6c0為首地址磅崭,對應的第一個對象是0x0000000101968888,也就是isa瓦哎。后面按順序排列的依次是各個屬性變量砸喻,每8字節(jié)一個對象柔逼,這也就是我們對齊原則。
需要注意的一點是我們的190.5是po不出來恩够,我們可以使用e -f f-- 0x4067d00000000000,或者p/f 0x4067d00000000000.因為我們正常的po打印不出來,double與float類型需要單獨輸出打印

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

剛才的例子中羡铲,我們發(fā)現(xiàn)第二個變量蜂桶,0x0000001200006261實際上是3個變量組成的,0x12也切、0x62扑媚、0x61,這個是由于進行了響應的內(nèi)存對齊雷恃,那我們就來看看結(jié)構(gòu)體的內(nèi)存對齊原則

1:數(shù)據(jù)成員對齊規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員疆股,第一個數(shù)據(jù)成員放在offset為0的地方,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員倒槐,比如說是數(shù)組旬痹,結(jié)構(gòu)體等)的整數(shù)倍開始(比如int在32位機為4字節(jié),則要從4的整數(shù)倍地址開始存儲。

2:結(jié)構(gòu)體作為成員:如果一個結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲.(struct a里存有struct b,b里有char,int ,double等元素,那b應該從8的整數(shù)倍開始存儲.)

3:收尾工作:結(jié)構(gòu)體的總大小,也就是sizeof的結(jié)果,.必須是其內(nèi)部最大成員的整數(shù)倍.不足的要補齊讨越。

我們先來看兩個結(jié)構(gòu)體

struct HZMStruct1 {
    double a;      
    char b;        
    int c;          
    short d;        
}struct1;

struct HZMStruct2 {
    double a;       
    int b;          
    char c;        
    short d;        
}struct2;

這兩個結(jié)構(gòu)體的成員變量完全一樣两残,只是順序不一樣,這樣的兩個結(jié)構(gòu)體的sizeof會一樣嘛把跨?直接上結(jié)果


02-結(jié)構(gòu)體的sizeof.png

Why人弓?

    double a;       // 8字節(jié)    存儲位置[0 7]
    char b;         // 1字節(jié)    [8]
    int c;          // 4字節(jié)    (9 10 11 [12 13 14 15]不是整數(shù)倍數(shù)的位置pass掉 
    short d;        // 2字節(jié)    [16 17] 24
}struct1;

struct HZMStruct2 {
    double a;       // 8字節(jié)    [0 7]
    int b;          // 4字節(jié)    [8 9 10 11]
    char c;         // 1字節(jié)    [12]
    short d;        // 2字節(jié)    (13 [14 15] 16
}struct2;

在我們存儲的過程中,其實蘋果會自動幫我們進行最優(yōu)化排序

在上面的過程中着逐,我們將不是當前對象的整數(shù)倍的存儲位置pass掉了崔赌,這是為什么?我們通過畫圖來理解


02-內(nèi)存對齊原因.png

當我們看到第一種取法耸别,發(fā)現(xiàn)每次變化取值長度健芭,一共需要3次才取完,而第二種取法直接按照最大長度去取秀姐,不夠的位置空出來吟榴,后續(xù)如果有滿足條件的在優(yōu)化的過程中插進去(1+4+3->1+3+4)這樣我們只需要兩次就全部取完了,這與我們第一篇文章講的字節(jié)對齊囊扳,以空間換時間吩翻,是異曲同工之妙。

下面我們再做一個練習鞏固下

struct LGStruct3 {
    double a;     // 8字節(jié)    [0 7]
    int b;        // 4字節(jié)    [8 9 10 11]
    char c;       // 1字節(jié)    [12]
    short d;      // 2字節(jié)    (13 [14 15]   
    int e;        // 4字節(jié)    [16 17 18 19]   24
    struct HZMStruct1 str;    (20 21 22 23 [24~ 41]  ->48
}struct3;

malloc源碼引入

02-LGPerson.png
02-malloc.png

第一個:<LGPerson: 0x100542960>應該不用過多解釋了
第二個:我們可以理解為person是個對象锥咸,對象的本質(zhì)就是指針地址狭瞎,指針大小為8字節(jié)
第三個:LGPerson的各個成員變量相加,8+8+4+8=28 ->32 但是要注意還有一個isa搏予,所以32+8=40
第四個:40->48是因為啥呢熊锭?我們看看malloc_size我們只能通過源碼進行分析
接下來就就還是進入我們的源碼文件進行分析

malloc分析探索思路

首先從alloc進入objc的源碼,找到obj = (id)calloc(1, size);
操作,涉及的方法順序是alloc --> _objc_rootAlloc --> callAlloc --> _objc_rootAllocWithZone --> _class_createInstanceFromZone

02-alloc源碼探索.png

這里calloc的探索需要切換到 libmalloc源碼中碗殷,可以在opensource下載最新版精绎,接著往下走

1、在可編譯的libmalloc中定義一個可編譯的target锌妻,在main中使用calloc創(chuàng)建一個指針

02-libmalloc源碼.png

2代乃、進入_malloc_zone_calloc的源碼實現(xiàn),關鍵代碼是1560行zone->calloc(zone, num_items, size);

02-_malloc_zone_calloc.png

3仿粹、進入zone->alloc的源碼搁吓,源碼就無法繼續(xù)跟進了


02-zone->alloc.png

重點:為了繼續(xù)深入了解,我們在ptr = zone->calloc(zone, num_items, size);處吭历,加一個斷點堕仔,然后運行。
斷住后晌区,通過打印得知zone->calloc的源碼實現(xiàn)在default_zone_calloc方法摩骨,然后全局搜索default_zone_calloc方法,找到具體實現(xiàn)

02-p zone->calloc.png

4朗若、進入calloc的源碼實現(xiàn)仿吞,其中主要由兩部分操作

  • 創(chuàng)建真正的zone,即runtime_default_zone方法
  • 使用真正的zone進行calloc
    02-default_zone_calloc.png

5捡偏、斷點走到return后唤冈,繼續(xù)打印


02-po zone->calloc.png

6、搜索nano_calloc進入银伟,其中的關鍵代碼是888行的返回值你虹,此時的p是pointer表示指針 和前面的 ptr一樣

02-nano_calloc關鍵代碼.png

7、進入_nano_malloc_check_clear源碼彤避,將if else 折疊傅物,看主流程

02-_nano_malloc_check_clear.png

其中segregated_next_block就是指針內(nèi)存開辟算法,目的是找到合適的內(nèi)存并返回
slot_bytes是加密算法的(其目的是為了讓加密算法更加安全琉预,本質(zhì)就是一串自定義的數(shù)字)

8董饰、進入segregated_next_block方法,這個方法主要就是獲取內(nèi)存指針

02-segregated_next_block.png

整個流程大概意思就是不斷循環(huán)查找能夠容納需要的大小的空間圆米,如果找到直接返回空間地址卒暂,如果找不到返回0。

9娄帖、進入segregated_size_to_fit加密算法源碼, 通過算法邏輯也祠,可以看出,其本質(zhì)就會16字節(jié)對齊算法

02-segregated_size_to_fit.png

所以在我們的堆里面近速,整個對象的內(nèi)存是以16字節(jié)對齊诈嘿,成員變量是以8字節(jié)對齊(結(jié)構(gòu)體內(nèi)部)堪旧,對象與對象之間因為是在整個內(nèi)存中,所以也是16字節(jié)對齊

02-zone->alloc.png

所以我們之前的輸出結(jié)果為 <LGPerson: 0x100542960> - 8 - 40 - 48 最后一項是48

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末奖亚,一起剝皮案震驚了整個濱河市淳梦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌昔字,老刑警劉巖爆袍,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異李滴,居然都是意外死亡螃宙,警方通過查閱死者的電腦和手機蛮瞄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門所坯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人挂捅,你說我怎么就攤上這事芹助。” “怎么了闲先?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵状土,是天一觀的道長。 經(jīng)常有香客問我伺糠,道長蒙谓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任训桶,我火速辦了婚禮累驮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舵揭。我一直安慰自己谤专,他們只是感情好,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布午绳。 她就那樣靜靜地躺著置侍,像睡著了一般刃鳄。 火紅的嫁衣襯著肌膚如雪滔蝉。 梳的紋絲不亂的頭發(fā)上蛹找,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天精肃,我揣著相機與錄音返顺,去河邊找鬼酌畜。 笑死羊异,一個胖子當著我的面吹牛怎顾,可吹牛的內(nèi)容都是我干的螟够。 我是一名探鬼主播灾梦,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼峡钓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了若河?” 一聲冷哼從身側(cè)響起能岩,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎萧福,沒想到半個月后拉鹃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡鲫忍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年膏燕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悟民。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡坝辫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出射亏,到底是詐尸還是另有隱情近忙,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布智润,位于F島的核電站及舍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏窟绷。R本人自食惡果不足惜锯玛,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望兼蜈。 院中可真熱鬧攘残,春花似錦、人聲如沸饭尝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钥平。三九已至实撒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涉瘾,已是汗流浹背知态。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留立叛,地道東北人负敏。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像秘蛇,于是被迫代替她去往敵國和親其做。 傳聞我的和親對象是個殘疾皇子顶考,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容