iOS中OC對(duì)象alloc分析

首先我們先看下alloc創(chuàng)建對(duì)象的一個(gè)整體流程圖:

alloc流程 (1).png

1. 如何分析 alloc 方法的執(zhí)行流程

1.1 通過符號(hào)斷點(diǎn)分析

首先我們?cè)赼lloc方法調(diào)用的地方打上斷點(diǎn)纱昧,待程序運(yùn)行到改行時(shí),按住 ctrl鍵,同時(shí)數(shù)遍點(diǎn)擊 xcode底部的 Debug窗口的 step into即可進(jìn)入。具體方法如下圖所示:

QQ20210605-231939@2x.png

注意:一定要在程序運(yùn)行到alloc方法的時(shí)候读拆,在enable符號(hào)斷點(diǎn)供汛,否者程序運(yùn)行時(shí)創(chuàng)建的類,會(huì)反復(fù)hit這個(gè)符號(hào)斷點(diǎn)

1.2 直接閱讀匯編代碼

通過設(shè)置Always Show Disassembly閱讀程序的匯編代碼悉尾,分析alloc的執(zhí)行流程。具體方法: 點(diǎn)擊xcode頂部的Debug菜單挫酿,選擇Debug Workflow,勾選Always Show Disassembly构眯。打上斷點(diǎn),重新運(yùn)行程序早龟,將直接進(jìn)入?yún)R編代碼窗口鸵赖。此方法可結(jié)合符號(hào)斷點(diǎn),快速定位到相關(guān)的方法拄衰。

323191622906873_.pic_hd.jpg

1.3 通過編譯源碼

此方法它褪,相較于閱讀匯編代碼,對(duì)于我個(gè)人而言比較簡(jiǎn)單翘悉,也比較直觀茫打,但源碼的編譯過程比較繁瑣,問題比較多妖混,需要很多耐心去解決相關(guān)的問題老赤。想走捷徑的同學(xué)可以去這里下載相關(guān)的代碼:Github。如果想嘗試下自己編輯源代碼制市,可以去這里下載objc4源碼抬旺,opensource

可能會(huì)遇到的問題:
1.3.1 設(shè)置了斷點(diǎn)卻無法觸發(fā)

選擇 Target -> Build Phases -> Compile Sourcesmain.m文件移動(dòng)到最前面。

QQ20210605-234044@2x.png
1.3.1 斷點(diǎn)生效了,但是無法進(jìn)入alloc方法的實(shí)現(xiàn)

選擇 Target -> Build Setting -> Enable Hardened Runtime將其設(shè)置為Yes

QQ20210605-234343@2x.png

2. alloc方法分析

首先會(huì)調(diào)用:

+ (id)alloc {
    return _objc_rootAlloc(self);
}

再次進(jìn)入會(huì)調(diào)用:

id _objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

再次進(jìn)入會(huì)調(diào)用:

static ALWAYS_INLINE id callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
    if (slowpath(checkNil && !cls)) return nil;
    // 這里的if判斷的是當(dāng)前類或者父類有沒有實(shí)現(xiàn)alloc/allocWithZone方法
    if (fastpath(!cls->ISA()->hasCustomAWZ())) { 
        return _objc_rootAllocWithZone(cls, nil);
    }
#endif

    // No shortcuts available.
    if (allocWithZone) {
        return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
    }
    return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}

點(diǎn)擊_objc_rootAllocWithZone會(huì)進(jìn)入

id _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
    // allocWithZone under __OBJC2__ ignores the zone parameter
    return _class_createInstanceFromZone(cls, 0, nil,
                                         OBJECT_CONSTRUCT_CALL_BADALLOC);
}

再次點(diǎn)擊_class_createInstanceFromZone就進(jìn)入了真在的核心方法祥楣。

static ALWAYS_INLINE id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                              int construct_flags = OBJECT_CONSTRUCT_NONE,
                              bool cxxConstruct = true,
                              size_t *outAllocatedSize = nil)
{
    ASSERT(cls->isRealized());

    // Read class's info bits all at once for performance
    bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();
    size_t size;

    size = cls->instanceSize(extraBytes); // 計(jì)算出需要的內(nèi)存空間的大小
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        obj = (id)calloc(1, size);// 開辟內(nèi)存空間
    }
    if (slowpath(!obj)) {
        if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }

    if (!zone && fast) {
        obj->initInstanceIsa(cls, hasCxxDtor);// isa指針跟cls關(guān)聯(lián)起來
    } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if (fastpath(!hasCxxCtor)) {
        return obj;
    }

    construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
    return object_cxxConstructFromClass(obj, cls, construct_flags);
}

三個(gè)核心的方法:

instanceSize: 先計(jì)算出需要的內(nèi)存空間大小

   inline size_t instanceSize(size_t extraBytes) const {
        if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
            return cache.fastInstanceSize(extraBytes);
        }

        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
    }
    // 快速計(jì)算
    size_t fastInstanceSize(size_t extra) const
    {
        ASSERT(hasFastInstanceSize(extra));

        if (__builtin_constant_p(extra) && extra == 0) {
            return _flags & FAST_CACHE_ALLOC_MASK16;
        } else {
            size_t size = _flags & FAST_CACHE_ALLOC_MASK;
            // 字節(jié)對(duì)齊
            return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
        }
    }

    // 計(jì)算類中變量所需的大小
    uint32_t alignedInstanceSize() const {
        return word_align(unalignedInstanceSize());
    }
       
       // 最后調(diào)用 align16 方法开财,進(jìn)行字節(jié)對(duì)齊汉柒,返回值必然是16的倍數(shù)
       // 這個(gè)方法寫的很巧妙、優(yōu)雅责鳍,值得學(xué)習(xí)碾褂,深究
    static inline size_t align16(size_t x) {
        return (x + size_t(15)) & ~size_t(15);
    }
    

calloc: 向系統(tǒng)申請(qǐng)開辟內(nèi)存,返回地址指針

instanceSize計(jì)算出所需的內(nèi)存空間大小后,交由系統(tǒng)去開辟相應(yīng)的內(nèi)存空間大小历葛。首先我們通過斷點(diǎn)可以看出正塌,obj在創(chuàng)建的時(shí)候就分配一塊臟地址。

QQ20210606-011151@2x.png

經(jīng)過調(diào)用calloc方法以后恤溶,開辟相應(yīng)的內(nèi)存空間乓诽,但這時(shí)候還并未與相應(yīng)的類關(guān)聯(lián)起來

QQ20210606-011503@2x.png

initInstanceIsa: 關(guān)聯(lián)到相應(yīng)的類

image.png

通過斷點(diǎn)的可以看到,initInstanceIsa調(diào)用以后咒程,已經(jīng)將isa指針與相應(yīng)的類關(guān)聯(lián)起來鸠天。最終返回obj,至此alloc的流程基本調(diào)用完畢。

3. 總結(jié)

通過以上的分析孵坚,我們可以得出調(diào)用alloc方法以后,會(huì)開辟出相應(yīng)大小的內(nèi)存空間窥淆。而調(diào)用init方法卖宠,會(huì)將相應(yīng)的地址指向該內(nèi)存空間。代碼驗(yàn)證:

     Person *p1 = [Person alloc];
     Person *p2 = [p1 init];
     Person *p3 = [p1 init];
     NSLog(@"%@-%p", p1, &p1); // <Person: 0x10103f560>-0x7ffeefbff440
     NSLog(@"%@-%p", p2, &p2); // <Person: 0x10103f560>-0x7ffeefbff428
     NSLog(@"%@-%p", p3, &p3); // <Person: 0x10103f560>-0x7ffeefbff430

     Person *p4 = [Person alloc];
     NSLog(@"%@-%p", p4, &p4); // <Person: 0x1014457b0>-0x7ffeefbff438

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末忧饭,一起剝皮案震驚了整個(gè)濱河市扛伍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌词裤,老刑警劉巖刺洒,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吼砂,居然都是意外死亡逆航,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門渔肩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來因俐,“玉大人,你說我怎么就攤上這事周偎∧ㄊ#” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵蓉坎,是天一觀的道長(zhǎng)澳眷。 經(jīng)常有香客問我,道長(zhǎng)蛉艾,這世上最難降的妖魔是什么钳踊? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任衷敌,我火速辦了婚禮,結(jié)果婚禮上箍土,老公的妹妹穿的比我還像新娘逢享。我一直安慰自己,他們只是感情好吴藻,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布瞒爬。 她就那樣靜靜地躺著,像睡著了一般沟堡。 火紅的嫁衣襯著肌膚如雪侧但。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天航罗,我揣著相機(jī)與錄音禀横,去河邊找鬼。 笑死粥血,一個(gè)胖子當(dāng)著我的面吹牛柏锄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播复亏,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼趾娃,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了缔御?” 一聲冷哼從身側(cè)響起抬闷,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎耕突,沒想到半個(gè)月后笤成,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡眷茁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年炕泳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片上祈。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喊崖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出雇逞,到底是詐尸還是另有隱情荤懂,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布塘砸,位于F島的核電站节仿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏掉蔬。R本人自食惡果不足惜廊宪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一矾瘾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧箭启,春花似錦壕翩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至荐操,卻和暖如春芜抒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背托启。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工宅倒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屯耸。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓拐迁,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親疗绣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子线召,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

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