對象是如何初始化的(iOS)

關注倉庫,及時得到更新:iOS-Source-Code-Analyzegit

Follow: Draveness · Githubgithub

在以前,咱們已經討論了很是多的問題了,關於 objc 源代碼系列的文章也快結束了,其實關於對象是如何初始化的這篇文章原本是我要寫的第一篇文章,可是因爲有不少前置內容不得不說,因此留到了這裏。ide

+ alloc- init 這一對咱們在 iOS 開發中天天都要用到的初始化方法一直困擾着我, 因而筆者仔細研究了一下 objc 源碼中 NSObject 如何進行初始化。函數

在具體分析對象的初始化過程以前,我想先放出結論,以避免文章中的細枝末節對讀者的理解有所影響;整個對象的初始化過程其實只是爲一個分配內存空間,而且初始化 isa_t 結構體的過程ui

alloc 方法分析

先來看一下 + alloc 方法的調用棧(在調用棧中省略了不少沒必要要的方法的調用):opencode

id _objc_rootAlloc(Class cls)
└── static id callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
    └── id class_createInstance(Class cls, size_t extraBytes)
             └── id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, bool cxxConstruct, size_t *outAllocatedSize)
            ├── size_t instanceSize(size_t extraBytes)
            ├── void    *calloc(size_t, size_t)
            └── inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)

這個調用棧中的方法涉及了多個文件中的代碼,在下面的章節中會對調用的方法逐步進行分析,若是這個調用棧讓你以爲很頭疼,也不是什麼問題。對象

alloc 的實現

+ (id)alloc {
    return _objc_rootAlloc(self);
}

alloc 方法的實現真的是很是的簡單, 它直接調用了另外一個私有方法 id _objc_rootAlloc(Class cls)內存

id _objc_rootAlloc(Class cls) {
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

這就是上帝類 NSObjectcallAlloc 的實現,咱們省略了很是多的代碼,展現了最多見的執行路徑:開發

static id callAlloc(Class cls, bool checkNil, bool allocWithZone=false) {
    id obj = class_createInstance(cls, 0);
    return obj;
}

id class_createInstance(Class cls, size_t extraBytes) {
    return _class_createInstanceFromZone(cls, extraBytes, nil);
}

對象初始化中最重要的操做都在 _class_createInstanceFromZone 方法中執行:get

static id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, bool cxxConstruct = true, size_t *outAllocatedSize = nil) {
    size_t size = cls->instanceSize(extraBytes);

    id obj = (id)calloc(1, size);
    if (!obj) return nil;
    obj->initInstanceIsa(cls, hasCxxDtor);

    return obj;
}

對象的大小

在使用 calloc 爲對象分配一塊內存空間以前,咱們要先獲取對象在內存的大小:

size_t instanceSize(size_t extraBytes) {
    size_t size = alignedInstanceSize() + extraBytes;
    if (size < 16) size = 16;
    return size;
}

uint32_t alignedInstanceSize() {
    return word_align(unalignedInstanceSize());
}

uint32_t unalignedInstanceSize() {
    assert(isRealized());
    return data()->ro->instanceSize;
}

實例大小 instanceSize 會存儲在類的 isa_t 結構體中,而後通過對齊最後返回。

Core Foundation 須要全部的對象的大小都必須大於或等於 16 字節。

在獲取對象大小以後,直接調用 calloc 函數就能夠爲對象分配內存空間了。

isa 的初始化

在對象的初始化過程當中除了使用 calloc 來分配內存以外,還須要根據類初始化 isa_t 結構體:

inline void objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor) { 
    if (!indexed) {
        isa.cls = cls;
    } else {
        isa.bits = ISA_MAGIC_VALUE;
        isa.has_cxx_dtor = hasCxxDtor;
        isa.shiftcls = (uintptr_t)cls >> 3;
    }
}

上面的代碼只是對 isa_t 結構體進行初始化而已:

union isa_t {
   isa_t() { }
   isa_t(uintptr_t value) : bits(value) { }
    
   Class cls;
   uintptr_t bits;
    
   struct {
       uintptr_t indexed           : 1;
       uintptr_t has_assoc         : 1;
       uintptr_t has_cxx_dtor      : 1;
       uintptr_t shiftcls          : 44;
       uintptr_t magic             : 6;
       uintptr_t weakly_referenced : 1;
       uintptr_t deallocating      : 1;
       uintptr_t has_sidetable_rc  : 1;
       uintptr_t extra_rc          : 8;
   };
};

在這裏並不想過多介紹關於 isa_t 結構體的內容,你能夠看從 NSObject 的初始化了解 isa 來了解你想知道的關於 isa_t 的所有內容。

init 方法

NSObject- init 方法只是調用了 _objc_rootInit 並返回了當前對象:

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

id _objc_rootInit(id obj) {
    return obj;
}

總結

在 iOS 中一個對象的初始化過程很符合直覺,只是分配內存空間、而後初始化 isa_t 結構體,其實現也並不複雜,這篇文章也是這個系列文章中較爲簡單而且簡短的一篇。

關注倉庫,及時得到更新:iOS-Source-Code-Analyze

Follow: Draveness · Github

原文連接: http://draveness.me/object-init

相關文章
相關標籤/搜索