iOS 底層探索- alloc流程

一、 OC的alloc初探

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

 NSObject *objc = [NSObject alloc];

在這里下好斷點(diǎn),打開(kāi)匯編調(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成立還沒(méi)有確定的探究,后續(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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容