iOS底層原理之a(chǎn)lloc探索

前言

alloc是iOS開發(fā)中為對(duì)象申請(qǐng)開辟內(nèi)存的方法,那么alloc的底層到底做了哪些,以及alloc是如何申請(qǐng)并且開辟內(nèi)存的,下面和大家一起探索一下alloc的具體步驟。

探索出alloc所在的源碼方法

  • 以下是三種常用的探索手法,也可以跳過直接從環(huán)境配置看起
    1. Control + in 找到 `objc_alloc
    1. 下符號(hào)斷點(diǎn):libobjc.A.dylib`+[NSObject alloc]
    1. 匯編查看流程

環(huán)境配置

準(zhǔn)備開車

  • 代碼準(zhǔn)備好,打上斷點(diǎn),command+左鍵 開始追蹤alloc方法,

    image.png

  • 1.進(jìn)入NSObject類中的alloc方法

+ (id)alloc {
    return _objc_rootAlloc(self);
}
  • 2.依然還是NSObject類中的_objc_rootAlloc方法,查看方法內(nèi)部,此時(shí)返回一個(gè)callAlloc對(duì)象
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
    1. 來到callAlloc內(nèi)部實(shí)現(xiàn),由于方法canAllocFast()的內(nèi)部調(diào)用了bits.canAllocFast(),其返回值為為固定false,所以可以確定之后創(chuàng)建對(duì)象會(huì)走class_createInstance方法,
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
 /* 省略代碼*/
        // canAllocFast返回值固定為false,內(nèi)部調(diào)用了一個(gè)bits. canAllocFast, 所以創(chuàng)建對(duì)象暫時(shí)看來只能用到else中的代碼
        if (fastpath(cls->canAllocFast())) {
            // No ctors, raw isa, etc. Go straight to the metal.
            bool dtor = cls->hasCxxDtor();
            id obj = (id)calloc(1, cls->bits.fastInstanceSize());
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            obj->initInstanceIsa(cls, dtor);
            return obj;
        }
        else {
            // Has ctor or raw isa or something. Use the slower path.
            id obj = class_createInstance(cls, 0);
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            return obj;
        }
   /* 省略代碼*/
}
  • 4.跟蹤class_createInstance(cls, 0)方法,其內(nèi)部調(diào)用了_class_createInstanceFromZone方法,并在其中進(jìn)行size計(jì)算,內(nèi)存申請(qǐng),以及isa初始化
id 
class_createInstance(Class cls, size_t extraBytes)
{
    return _class_createInstanceFromZone(cls, extraBytes, nil);
}
    1. 對(duì)象size計(jì)算,通過方法cls->instanceSize(extraBytes),計(jì)算出size,
      其中64位系統(tǒng)下,對(duì)象大小采用8字節(jié)對(duì)齊,但是實(shí)際申請(qǐng)的內(nèi)存最低為16字節(jié),也就是說系統(tǒng)分配內(nèi)存按照16字節(jié)對(duì)齊分配
// 此方法,用來字節(jié)對(duì)齊
uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }
// 字節(jié)對(duì)齊的詳細(xì)方法,WORD_MASK在64位下為7,32位下為3,用來進(jìn)行字節(jié)對(duì)齊
static inline size_t word_align(size_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

// 申請(qǐng)地址,此處有16字節(jié)限制
size_t instanceSize(size_t extraBytes) {
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
}
    1. 根據(jù)不同的條件,使用calloc或者malloc_zone_calloc進(jìn)行內(nèi)存申請(qǐng),并且初始化isa指針,至此size大小的對(duì)象obj已經(jīng)申請(qǐng)完成,并且返回
id obj;
    if (!zone  &&  fast) {
        obj = (id)calloc(1, size);
        if (!obj) return nil;
        obj->initInstanceIsa(cls, hasCxxDtor);
    } 
    else {
        if (zone) {
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;

        // Use raw pointer isa on the assumption that they might be 
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

小擴(kuò)展

init方法
  • 跟隨源碼,進(jìn)入init方法內(nèi)部,得出結(jié)論,init的唯一作用就是規(guī)范代碼,交給子類去自定義重寫,其他沒有實(shí)質(zhì)性的功能作用
- (id)init {
    return _objc_rootInit(self);
}

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;
}
new方法
  • 進(jìn)入new方法的內(nèi)部,可以發(fā)現(xiàn),new方法實(shí)際上做了兩步操作,先調(diào)用callAlloc方法申請(qǐng)內(nèi)存,再調(diào)用init方法,其中callAlloc就是我們?cè)谔剿鱝lloc的過程中第三步所做的操作,所以可以得出結(jié)論,new = alloc + init
+ (id)new {
    return [callAlloc(self, false/*checkNil*/) init];
}

最后附上一張簡(jiǎn)單的流程圖

image.png

總結(jié)

這是個(gè)人的實(shí)際追蹤源碼得出的一些結(jié)論,源碼中還有一些分支代碼還遠(yuǎn)遠(yuǎn)沒有被追蹤到,包括isa的初始化,allocWithZone等,如果有錯(cuò)誤的地方還請(qǐng)指正,大家一起討論,代碼之路,任重道遠(yuǎn)~

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,621評(píng)論 1 32
  • 1.OC里用到集合類是什么? 基本類型為:NSArray,NSSet以及NSDictionary 可變類型為:NS...
    輕皺眉頭淺憂思閱讀 1,469評(píng)論 0 3
  • 1.設(shè)計(jì)模式是什么? 你知道哪些設(shè)計(jì)模式,并簡(jiǎn)要敘述?設(shè)計(jì)模式是一種編碼經(jīng)驗(yàn),就是用比較成熟的邏輯去處理某一種類型...
    龍飝閱讀 2,294評(píng)論 0 12
  • 探尋OC對(duì)象的本質(zhì),我們平時(shí)編寫的Objective-C代碼,底層實(shí)現(xiàn)其實(shí)都是C\C++代碼,如圖所示: OC的對(duì)...
    二豬哥閱讀 616評(píng)論 0 6
  • 我是一個(gè)不愛用電腦打字的人,總覺得鍵盤發(fā)出的聲音會(huì)打擾思緒。喜歡用鉛筆寫字或者鋼筆在喜歡的本子上寫字??赡苁且?yàn)樾?..
    zcapricccilisa閱讀 264評(píng)論 0 0

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