alloc&init&new源碼分析

本文只做個人筆記使用,引用部分會在文末著名來源

alloc+inti整體流程:


alloc+init源碼流程

alloc核心三步:
1.先計算出需要的內(nèi)存大小空間;
2.向系統(tǒng)申請開辟內(nèi)存,返回地址指針;
3.將cls類與obj指針關(guān)聯(lián);

instanceSize的源碼中實現(xiàn)內(nèi)存大小

size_t instanceSize(size_t extraBytes) const {
   //編譯器快速計算內(nèi)存大小
   if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
       return cache.fastInstanceSize(extraBytes);
   }
   
   // 計算類中所有屬性的大小 + 額外的字節(jié)數(shù)0
   size_t size = alignedInstanceSize() + extraBytes;
   // CF requires all objects be at least 16 bytes.
   //如果size 小于 16,最小取16
   if (size < 16) size = 16;
   return size;
}

通過斷點調(diào)試,會執(zhí)行cache.fastInstanceSize方法,快速計算內(nèi)存大小

fastInstanceSize源碼中,會執(zhí)行到align16

size_t fastInstanceSize(size_t extra) const
{
    ASSERT(hasFastInstanceSize(extra));

    //Gcc的內(nèi)建函數(shù) __builtin_constant_p 用于判斷一個值是否為編譯時常數(shù),如果參數(shù)EXP 的值是常數(shù),函數(shù)返回 1,否則返回 0
    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
        //刪除由setFastInstanceSize添加的FAST_CACHE_ALLOC_DELTA16 8個字節(jié)
        return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
    }
}

align16的源碼中,這個方法是16字節(jié)對齊算法

//16字節(jié)對齊算法
static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}

內(nèi)存對齊原則:
1:數(shù)據(jù)成員對?規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第
?個數(shù)據(jù)成員放在offset為0的地?,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員??或者成員的?成員??(只要該成員有?成員,?如說是數(shù)組,結(jié)構(gòu)體等)的整數(shù)倍開始(?如int為4字節(jié),則要從4的整數(shù)倍地址開始存儲。 min(當前開始的位置m n) m = 9 n = 4 9 10 11 12
2:結(jié)構(gòu)體作為成員:如果?個結(jié)構(gòu)?有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最?元素??的整數(shù)倍地址開始存儲.(struct a?存有struct b,b?有char,int ,double等元素,那b應(yīng)該從8的整數(shù)倍開始存儲.)
3:收尾?作:結(jié)構(gòu)體的總??,也就是sizeof的結(jié)果,必須是其內(nèi)部最?
成員的整數(shù)倍,不?的要補?。

為什么需要16字節(jié)對齊

需要字節(jié)對齊的原因,有以下幾點:

通常內(nèi)存是由一個個字節(jié)組成的,cpu在存取數(shù)據(jù)時,并不是以字節(jié)為單位存儲,而是以塊為單位存取,塊的大小為內(nèi)存存取力度。頻繁存取字節(jié)未對齊的數(shù)據(jù),會極大降低cpu的性能,所以可以通過減少存取次數(shù)來降低cpu的開銷
16字節(jié)對齊,是由于在一個對象中,第一個屬性isa占8字節(jié),當然一個對象肯定還有其他屬性,當無屬性時,會預(yù)留8字節(jié),即16字節(jié)對齊,如果不預(yù)留,相當于這個對象的isa和其他對象的isa緊挨著,容易造成訪問混亂
16字節(jié)對齊后,可以加快CPU讀取速度,同時使訪問更安全,不會產(chǎn)生訪問混亂的情況
字節(jié)對齊-總結(jié)

在字節(jié)對齊算法中,對齊的主要是對象,而對象的本質(zhì)則是一個 struct objc_object的結(jié)構(gòu)體,
結(jié)構(gòu)體在內(nèi)存中是連續(xù)存放的,所以可以利用這點對結(jié)構(gòu)體進行強轉(zhuǎn)。
蘋果早期是8字節(jié)對齊,現(xiàn)在是16字節(jié)對齊。

總結(jié):
1.通過對alloc源碼的分析可以得知alloc的只要目的就是開辟內(nèi)存,而且開辟的內(nèi)存需要使用16字節(jié)對齊的算法,現(xiàn)在開辟的內(nèi)存基本上都是16的整數(shù)倍;
2.開辟內(nèi)存的核心步驟有三步:計算內(nèi)存大小-申請內(nèi)存-將isa與cls關(guān)聯(lián)

init源碼探索
類方法init

+ (id)init {
    return (id)self;
} 

這里的init是一個構(gòu)造方法 ,是通過工廠設(shè)計(工廠方法模式),主要是用于給用戶提供構(gòu)造方法入口。這里能使用id強轉(zhuǎn)的原因,主要還是因為 內(nèi)存字節(jié)對齊后,可以使用類型強轉(zhuǎn)為你所需的類型

實例方法init

- (id) init {
    return _objc_rootInit(self);
}

進入_objc_rootInit源碼

id _objc_rootInit(id obj)
{
    // In practice, it will be hard to rely on this function.
    // Many classes do not properly chain -init calls.
    return obj;
}

返回的是傳入的 self本身

new源碼
一般在開發(fā)中,初始化除了init,還可以使用new,兩者本質(zhì)上并沒有什么區(qū)別,以下是objc中new的源碼實現(xiàn),通過源碼可以得知,new函數(shù)中直接調(diào)用了callAlloc函數(shù)(即alloc中分析的函數(shù)),且調(diào)用了init函數(shù),所以可以得出new 其實就等價于 [alloc init]的結(jié)論 .

+ (id)new {
    return [callAlloc(self, false/*checkNil*/) init];
}

一般開發(fā)中并不建議使用new,主要是因為有時會重寫init方法做一些自定義的操作,例如 initWithXXX,會在這個方法中調(diào)用[super init],用new初始化可能會無法走到自定義的initWithXXX部分。

參考鏈接:http://www.itdecent.cn/p/b72018e88a97

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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