iOS 底層探索- alloc流程

一培己、 OC的alloc初探

代碼準(zhǔn)備巾乳,我們先對(duì)一個(gè)類alloc一個(gè)對(duì)象出來(lái)

 NSObject *objc = [NSObject alloc];

在這里下好斷點(diǎn)主穗,打開匯編調(diào)試(Debug->Debug workflow->Always Show Disassembly) 運(yùn)行茎刚,

圖1

我們可以看到下一條指令的匯編注釋symbol stub for: objc_alloc得知立即調(diào)用存根符號(hào)為objc_alloc的函數(shù)悲关,所以我們不妨增加一個(gè)objc_alloc的符號(hào)斷點(diǎn)谎僻,接著運(yùn)行可以看到

圖2

所以可以看到在調(diào)用NSObject *objc = [NSObject alloc];時(shí),其實(shí)調(diào)用的是objc中的objc_alloc方法坚洽。

二戈稿、OC 的 alloc 深入探究

經(jīng)過(guò)上面一步,我們就能淺嘗輒止嗎讶舰?當(dāng)然不能鞍盗;所以我搞來(lái)一份objc4源碼繼續(xù)我們的探究。
看到源碼 NSObject.mm 文件跳昼,里面的alloc方法

圖3

他會(huì)調(diào)用_objc_rootAlloc
圖4

  • 問(wèn)題:
    我上面看到 [NSObject alloc]看到的不是會(huì)到objc_alloc來(lái)嗎般甲?怎么源碼分析的到了_objc_rootAlloc?
  • 答:我們知道OC代碼有運(yùn)行時(shí)的特點(diǎn)鹅颊,即方法的調(diào)用不是在編譯時(shí)確定的敷存,而是運(yùn)行時(shí),不懂的補(bǔ)一下編譯原理堪伍。我們?cè)趏bjc運(yùn)行時(shí)源碼中查找objc_alloc锚烦,發(fā)現(xiàn)如下的代碼
static void 
fixupMessageRef(message_ref_t *msg)
{    
    msg->sel = sel_registerName((const char *)msg->sel);

    if (msg->imp == &objc_msgSend_fixup) { 
        if (msg->sel == @selector(alloc)) {
            msg->imp = (IMP)&objc_alloc;
        } else if (msg->sel == @selector(allocWithZone:)) {
            msg->imp = (IMP)&objc_allocWithZone;
        } else if (msg->sel == @selector(retain)) {
            msg->imp = (IMP)&objc_retain;
        } else if (msg->sel == @selector(release)) {
            msg->imp = (IMP)&objc_release;
        } else if (msg->sel == @selector(autorelease)) {
            msg->imp = (IMP)&objc_autorelease;
        } else {
            msg->imp = &objc_msgSend_fixedup;
        }
    } 
    else if (msg->imp == &objc_msgSendSuper2_fixup) { 
        msg->imp = &objc_msgSendSuper2_fixedup;
    } 
    else if (msg->imp == &objc_msgSend_stret_fixup) { 
        msg->imp = &objc_msgSend_stret_fixedup;
    } 
    else if (msg->imp == &objc_msgSendSuper2_stret_fixup) { 
        msg->imp = &objc_msgSendSuper2_stret_fixedup;
    } 
#if defined(__i386__)  ||  defined(__x86_64__)
    else if (msg->imp == &objc_msgSend_fpret_fixup) { 
        msg->imp = &objc_msgSend_fpret_fixedup;
    } 
#endif
#if defined(__x86_64__)
    else if (msg->imp == &objc_msgSend_fp2ret_fixup) { 
        msg->imp = &objc_msgSend_fp2ret_fixedup;
    } 
#endif
}

發(fā)現(xiàn)罅隙發(fā)送時(shí),當(dāng)msg->imp == &objc_msgSend_fixup 進(jìn)行了方法實(shí)現(xiàn)(IMP)的替換帝雇。

什么時(shí)候msg->imp == &objc_msgSend_fixup成立還沒有確定的探究涮俄,后續(xù)了解會(huì)進(jìn)一步更新。從源碼的運(yùn)行看[NSObject alloc]會(huì)走到objc_alloc尸闸;但是其子類的[LGPerson alloc]會(huì)調(diào)用_objc_rootAlloc

不管是_objc_rootAlloc或者objc_alloc從圖4看都會(huì)進(jìn)入callAlloc這個(gè)函數(shù),不過(guò)最后兩個(gè)參數(shù)不同彻亲;我們姑且往下走,進(jìn)入callAlloc去看看究竟。代碼如下

圖5
  • 這里看到有兩個(gè)宏定義函數(shù):slowpath, fastpath. 這兩個(gè)函數(shù)是用于編譯器優(yōu)化吮廉;slowpath低概率會(huì)走;fastpath大概率會(huì)走,如果項(xiàng)目中有需求苞尝,自己也可以用上,讓自己的代碼質(zhì)量提高宦芦。
  • hasCustomAWZ是否有自定義的AWZ宙址;經(jīng)過(guò)運(yùn)行實(shí)際得出,當(dāng)類的第一次運(yùn)行時(shí)slowpath踪旷,fastpath判斷都為false,后面在運(yùn)行時(shí)fastpath為true;不過(guò)我們看到下面的函數(shù)又是消息的發(fā)送曼氛,調(diào)用的是allocallocWithZone豁辉,其實(shí)接著往下走,可以發(fā)現(xiàn)舀患,他又回到了callAlloc,并且此時(shí)的fastpath為true;

那么函數(shù)此時(shí)應(yīng)來(lái)到_objc_rootAllocWithZone

unsigned 
class_createInstances(Class cls, size_t extraBytes, 
                      id *results, unsigned num_requested)
{
    return _class_createInstancesFromZone(cls, extraBytes, nil, 
                                          results, num_requested);
}

那么我們?nèi)タ纯?code>_class_createInstancesFromZone

unsigned
_class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone, 
                               id *results, unsigned num_requested)
{
    unsigned num_allocated;
    if (!cls) return 0;

    size_t size = cls->instanceSize(extraBytes);

    num_allocated = 
        malloc_zone_batch_malloc((malloc_zone_t *)(zone ? zone : malloc_default_zone()), 
                                 size, (void**)results, num_requested);
    for (unsigned i = 0; i < num_allocated; i++) {
        bzero(results[i], size);
    }

    // Construct each object, and delete any that fail construction.

    unsigned shift = 0;
    bool ctor = cls->hasCxxCtor();
    for (unsigned i = 0; i < num_allocated; i++) {
        id obj = results[i];
        obj->initIsa(cls);    // fixme allow nonpointer
        if (ctor) {
            obj = object_cxxConstructFromClass(obj, cls,
                                               OBJECT_CONSTRUCT_FREE_ONFAILURE);
        }
        if (obj) {
            results[i-shift] = obj;
        } else {
            shift++;
        }
    }

    return num_allocated - shift;    
}

這里的邏輯大體分為

  • 1徽级、求出實(shí)例所需內(nèi)存大小
size_t size = cls->instanceSize(extraBytes);
  • 2、分配空間
num_allocated = 
        malloc_zone_batch_malloc((malloc_zone_t *)(zone ? zone : malloc_default_zone()), 
                                 size, (void**)results, num_requested);

3聊浅、綁定isa到類

obj->initIsa(cls);    // fixme allow nonpointer

接下來(lái)返回對(duì)應(yīng)類的首地址

三餐抢、總結(jié)

  • NSObject 類
    alloc具體流程
    alloc ==> objc_alloc ==> callAlloc ==> _objc_rootAllocWithZone ==> _class_createInstanceFromZone ==>(1、獲取實(shí)例大小size;2低匙、分配size;3旷痕、initInstanceIsa)

  • NSObject 繼承類
    alloc具體流程
    alloc ==> _objc_rootAlloc ==> callAlloc ==> objc_msgSend ==> _objc_rootAllocWithZone ==> _class_createInstanceFromZone ==>(1、獲取實(shí)例大小size;2顽冶、分配size;3欺抗、initInstanceIsa)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市强重,隨后出現(xiàn)的幾起案子绞呈,更是在濱河造成了極大的恐慌,老刑警劉巖间景,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件佃声,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡倘要,警方通過(guò)查閱死者的電腦和手機(jī)圾亏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)封拧,“玉大人志鹃,你說(shuō)我怎么就攤上這事≡笪鳎” “怎么了弄跌?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)尝苇。 經(jīng)常有香客問(wèn)我,道長(zhǎng)埠胖,這世上最難降的妖魔是什么糠溜? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮直撤,結(jié)果婚禮上非竿,老公的妹妹穿的比我還像新娘。我一直安慰自己谋竖,他們只是感情好红柱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布承匣。 她就那樣靜靜地躺著,像睡著了一般锤悄。 火紅的嫁衣襯著肌膚如雪韧骗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天零聚,我揣著相機(jī)與錄音袍暴,去河邊找鬼。 笑死隶症,一個(gè)胖子當(dāng)著我的面吹牛政模,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蚂会,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼淋样,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了胁住?” 一聲冷哼從身側(cè)響起趁猴,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎措嵌,沒想到半個(gè)月后躲叼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡企巢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年枫慷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浪规。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡或听,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出笋婿,到底是詐尸還是另有隱情誉裆,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布缸濒,位于F島的核電站足丢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏庇配。R本人自食惡果不足惜斩跌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捞慌。 院中可真熱鬧耀鸦,春花似錦、人聲如沸啸澡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至洛姑,卻和暖如春上沐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吏口。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工奄容, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人产徊。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓昂勒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親舟铜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子戈盈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354