OC底層01:alloc底層分析

開發(fā)中,我們經(jīng)常使用到[[xxx alloc] init],但卻沒有真正理解過alloc,init,現(xiàn)在進(jìn)行深入了解一下搓扯。

alloc,init的功能

1.先看看這樣的一個(gè)例子
//創(chuàng)建一個(gè)Person類,然后引用對(duì)應(yīng)頭文件
        Person *p1 = [Person alloc];
        Person *p2 = [p1 init];
        Person *p3 = [p1 init];
        
        NSLog(@"%p-%p", p1, &p1);
        NSLog(@"%p-%p", p2, &p2);
        NSLog(@"%p-%p", p3, &p3);

運(yùn)行結(jié)果如圖:
運(yùn)行結(jié)果
  • p1,p2,p3的指向地址都相同
  • p1,p2,p3的地址不同
2.alloc與init的關(guān)系如圖
  • alloc生成對(duì)象地址礁芦。
  • init分配指針地址,并指向?qū)ο蟮刂?/li>

alloc流程

需要看到alloc流程,可到蘋果開源網(wǎng)站下載。
如果要將源碼進(jìn)行編譯調(diào)試,可參見

對(duì)源碼進(jìn)行分析

從alloc方法一步一步點(diǎn)進(jìn)去我們分別能看到:

//NSObject.mm文件中  +alloc方法
+ (id)alloc {
    return _objc_rootAlloc(self);
}

//NSObject.mm文件中  _objc_rootAlloc(Class cls)方法
id _objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

//NSObject.mm文件中  callAlloc方法 
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
    if (slowpath(checkNil && !cls)) return nil;
    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));
}

//objc-runtime-new.mm文件中  _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)方法 
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);
}

//objc-runtime-new.mm文件中 _class_createInstanceFromZone方法 
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);
    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);
    }
    if (slowpath(!obj)) {
        if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }

    if (!zone && fast) {
        obj->initInstanceIsa(cls, hasCxxDtor);
    } 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);
}

1. 前幾步一條路進(jìn)入可以不提棍矛,在callAlloc方法中蕴掏,出現(xiàn)了分支slowpathfastpath门坷,快速路徑與慢速路徑默勾,可以理解為大多數(shù)情況走快速路徑,極少情況走慢速路徑乍恐,這樣寫便于運(yùn)行效率的提升评疗,所以我們繼續(xù)研究_objc_rootAllocWithZone方法。
2. 緊接著我們到了關(guān)鍵的_class_createInstanceFromZone方法:
  1. _class_createInstanceFromZone第一個(gè)研究的方法size = cls->instanceSize(extraBytes);
    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;
    }

    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;
            // remove the FAST_CACHE_ALLOC_DELTA16 that was added
            // by setFastInstanceSize
            return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
        }
    }

經(jīng)過調(diào)試茵烈,我們能看到進(jìn)入fastInstanceSize方法壤巷,然后執(zhí)行return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);

static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}

很明顯,這是在進(jìn)行內(nèi)存對(duì)齊(下一章節(jié)具體講解)

3. 得到一個(gè)16字節(jié)倍數(shù)大小size瞧毙,然后執(zhí)行obj = (id)calloc(1, size);分配內(nèi)存空間(ps:if (zone)現(xiàn)已廢棄胧华,不做過深研究)

當(dāng)我們?cè)趫?zhí)行obj = (id)calloc(1, size);后我們答應(yīng)obj可以看到:


而我們一個(gè)對(duì)象的打印的格式應(yīng)該為<Person: 0x10060de90>,所以calloc只是分配了一個(gè)地址空間,并沒有創(chuàng)建新的指針指向它宙彪。也證明了前面alloc的說法

4.當(dāng)我們執(zhí)行完obj->initInstanceIsa(cls, hasCxxDtor);我們拿到我們想要的結(jié)果:

綜上alloc運(yùn)行流程如圖:


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末矩动,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子释漆,更是在濱河造成了極大的恐慌悲没,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件男图,死亡現(xiàn)場(chǎng)離奇詭異示姿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)逊笆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門栈戳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人难裆,你說我怎么就攤上這事子檀。” “怎么了乃戈?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵褂痰,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我症虑,道長(zhǎng)缩歪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任谍憔,我火速辦了婚禮匪蝙,結(jié)果婚禮上苟翻,老公的妹妹穿的比我還像新娘。我一直安慰自己骗污,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布沈条。 她就那樣靜靜地躺著需忿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蜡歹。 梳的紋絲不亂的頭發(fā)上屋厘,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音月而,去河邊找鬼汗洒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛父款,可吹牛的內(nèi)容都是我干的溢谤。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼憨攒,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼世杀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肝集,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤瞻坝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后杏瞻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體所刀,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年捞挥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了浮创。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡砌函,死狀恐怖蒸矛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情胸嘴,我是刑警寧澤雏掠,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站劣像,受9級(jí)特大地震影響乡话,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜耳奕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一绑青、第九天 我趴在偏房一處隱蔽的房頂上張望诬像。 院中可真熱鬧,春花似錦闸婴、人聲如沸坏挠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽降狠。三九已至,卻和暖如春庇楞,著一層夾襖步出監(jiān)牢的瞬間榜配,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工吕晌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蛋褥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓睛驳,卻偏偏與公主長(zhǎng)得像烙心,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乏沸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351