iOS底層探索之對(duì)象原理(一)

前言

對(duì)象創(chuàng)建alloc涛救,alloc是iOS開發(fā)中為對(duì)象申請(qǐng)開辟內(nèi)存的方法酸茴,那么alloc的底層到底做了哪些磺陡,以及alloc是如何申請(qǐng)并且開辟內(nèi)存的让簿,下面和大家一起探索一下alloc的具體步驟敬察。

alloc探索思路

以下是三種常用的探索手法,也可以跳過直接從alloc底層原理看起

TDPerson *p1 = [TDPerson alloc];
TDPerson *p2 = [p1 init];
TDPerson *p3 = [p1 init];

NSLog(@"%@ —— %p", p1, &p1)
NSLog(@"%@ —— %p", p2, &p2)
NSLog(@"%@ —— %p", p3, &p3)

輸出:
<TDPerson: 0x6000032f0218> —— 0x6000032f038
<TDPerson: 0x6000032f0218> —— 0x6000032f030
<TDPerson: 0x6000032f0218> —— 0x6000032f028

1尔当、下斷點(diǎn)

control + in 找到 libobjc.A.dylib`objc_alloc (注: 建議用真機(jī)莲祸,模擬器走x86,真機(jī)走的arm64)

2椭迎、下符號(hào)斷點(diǎn)(Symbolic Breakpoint)

斷點(diǎn) alloc { 找到 libobjc.A.dylib`+[NSObject alloc]

3锐帜、匯編

打開匯編查看流程 Debug ——> Workflow ——> always show Disassembly

alloc底層原理

上面比較難斷點(diǎn)跟蹤,為了更加直觀的看到objc源碼中的方法調(diào)用畜号,方便我們斷點(diǎn)跟蹤缴阎,因此配置出了可直接編譯運(yùn)行的源碼,配置如:objc4-750源碼 + Xcode11 + MacOS 10.15 ios最新支持ios10.14
官方源碼下載地址
iOS_objc4-756.2 最新源碼編譯調(diào)試

代碼準(zhǔn)備好简软,打上斷點(diǎn)蛮拔,command+左鍵 開始追蹤alloc方法,進(jìn)入如下流程:

  • alloc
+ (id)alloc {
    return _objc_rootAlloc(self);
}
  • _objc_rootAlloc
_objc_rootAlloc(Class cls)  {
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
  • callAlloc
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
    if (slowpath(checkNil && !cls)) return nil;
#if __OBJC2__
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        // canAllocFast返回值固定為false痹升,內(nèi)部調(diào)用了一個(gè)bits.canAllocFast, 所以創(chuàng)建對(duì)象暫時(shí)看來只能用到else中的代碼
        if (fastpath(cls->canAllocFast())) {
            // No ctors, raw isa, etc. Go straight to the metal.
            bool dtor = cls->hasCxxDtor();
            id obj = (id)calloc(1, cls->bits.fastInstanceSize());
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            obj->initInstanceIsa(cls, dtor);
            return obj;
        }
        else {
            // Has ctor or raw isa or something. Use the slower path.
            id obj = class_createInstance(cls, 0);
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            return obj;
        }
    }
#endif
    if (allocWithZone) return [cls allocWithZone:nil];
    return [cls alloc];
}
  • class_createInstance
id 
class_createInstance(Class cls, size_t extraBytes) {
    return _class_createInstanceFromZone(cls, extraBytes, nil);
}
  • _class_createInstanceFromZone

在_class_createInstanceFromZone中進(jìn)行對(duì)象size計(jì)算instanceSize建炫,內(nèi)存申請(qǐng)calloc,以及isa初始化initInstanceIsa

_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, \
                              bool cxxConstruct = true, size_t *outAllocatedSize = nil)
{
    /* 省略代碼 */
    size_t size = cls->instanceSize(extraBytes);     // 對(duì)象size計(jì)算
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (!zone && fast) { 
        obj = (id)calloc(1, size);                  // 開辟了內(nèi)存空間
        if (!obj) return nil;
        obj->initInstanceIsa(cls, hasCxxDtor)       // 讓obj和當(dāng)前的class關(guān)聯(lián)起來
    } else {
        if (zone) {
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;
        // Use raw pointer isa on the assumption that they might be 
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }
    /* 省略代碼 */
}

alloc申請(qǐng)內(nèi)存(字節(jié)對(duì)齊)

size_t size = cls->instanceSize(extraBytes); size是如何開辟內(nèi)存空間呢

  1. 我們對(duì)象需要的內(nèi)存空間 以 8位倍數(shù)進(jìn)行存儲(chǔ) 以 8字節(jié)對(duì)齊
  2. 最少16字節(jié)

好處:方便計(jì)算機(jī)讀取疼蛾,以空間換取時(shí)間

// 申請(qǐng)地址肛跌,此處有16字節(jié)限制
size_t instanceSize(size_t extraBytes) { 
    size_t size = alignedInstanceSize() + extraBytes;
    // CF requires all objects be at least 16 bytes.
    if (size < 16) size = 16;
    return size;
}

// 此方法,用來字節(jié)對(duì)齊
uint32_t alignedInstanceSize() {
    return word_align(unalignedInstanceSize()); 
}

// 字節(jié)對(duì)齊的詳細(xì)方法察郁,WORD_MASK在64位下為7衍慎,32位下為3,用來進(jìn)行字節(jié)對(duì)齊
static inline size_t word_align(size_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

小擴(kuò)展

init方法

查看init源碼绳锅,init 直接 return obj; 規(guī)范代碼西饵,工廠設(shè)計(jì)模式,交給子類去自定義重寫鳞芙,沒有其他實(shí)質(zhì)功能作用

- (id)init {
    return _objc_rootInit(self);
}
new方法

查看new源碼眷柔,內(nèi)部調(diào)用callAlloc 后執(zhí)行init,得出結(jié)論:new = alloc + init

+ (id)new {
    return [callAlloc(self, false/*checkNil*/) init];
}
對(duì)象屬性在內(nèi)存中的布局

如何查看對(duì)象的存儲(chǔ)空間 內(nèi)存 run原朝,常用LLDB命令如:

  • register read:讀寄存器驯嘱;x0即是第一個(gè)參數(shù)的傳遞者,也是返回值的存儲(chǔ)地方喳坠。
  • x object:以16進(jìn)制打印這個(gè)對(duì)象的地址空間鞠评,iOS是小端模式 , 內(nèi)存 run 是反的壕鹉。
  • x/4xg object:以16進(jìn)制的方式打印 object 的 4 段內(nèi)存區(qū)域的地址剃幌,每一段是 8 個(gè)字節(jié)大小聋涨。

更多LLDB命令可移步參考iOS之LLDB常用命令

附上alloc流程圖

alloc流程圖.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者负乡。
  • 序言:七十年代末牍白,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子抖棘,更是在濱河造成了極大的恐慌茂腥,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件切省,死亡現(xiàn)場(chǎng)離奇詭異最岗,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)朝捆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門般渡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人右蹦,你說我怎么就攤上這事诊杆。” “怎么了何陆?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵晨汹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我贷盲,道長(zhǎng)淘这,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任巩剖,我火速辦了婚禮铝穷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘佳魔。我一直安慰自己曙聂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布鞠鲜。 她就那樣靜靜地躺著宁脊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪贤姆。 梳的紋絲不亂的頭發(fā)上榆苞,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音霞捡,去河邊找鬼坐漏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的赊琳。 我是一名探鬼主播街夭,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼慨畸!你這毒婦竟也來了莱坎?” 一聲冷哼從身側(cè)響起衣式,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤寸士,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后碴卧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弱卡,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年住册,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了婶博。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荧飞,死狀恐怖凡人,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情叹阔,我是刑警寧澤挠轴,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站耳幢,受9級(jí)特大地震影響岸晦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜睛藻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一启上、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧店印,春花似錦冈在、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至院峡,卻和暖如春兴使,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背照激。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工发魄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓励幼,卻偏偏與公主長(zhǎng)得像汰寓,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子苹粟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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