關注倉庫,及時得到更新:iOS-Source-Code-Analyzegit
Follow: Draveness · Githubgithub
在以前,咱們已經討論了很是多的問題了,關於 objc 源代碼系列的文章也快結束了,其實關於對象是如何初始化的這篇文章原本是我要寫的第一篇文章,可是因爲有不少前置內容不得不說,因此留到了這裏。ide
+ alloc
和 - init
這一對咱們在 iOS 開發中天天都要用到的初始化方法一直困擾着我, 因而筆者仔細研究了一下 objc 源碼中 NSObject
如何進行初始化。函數
在具體分析對象的初始化過程以前,我想先放出結論,以避免文章中的細枝末節對讀者的理解有所影響;整個對象的初始化過程其實只是爲一個分配內存空間,而且初始化 isa_t 結構體的過程。ui
先來看一下 + 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)
這個調用棧中的方法涉及了多個文件中的代碼,在下面的章節中會對調用的方法逐步進行分析,若是這個調用棧讓你以爲很頭疼,也不是什麼問題。對象
+ (id)alloc { return _objc_rootAlloc(self); }
alloc
方法的實現真的是很是的簡單, 它直接調用了另外一個私有方法 id _objc_rootAlloc(Class cls)
內存
id _objc_rootAlloc(Class cls) { return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/); }
這就是上帝類 NSObject
對 callAlloc
的實現,咱們省略了很是多的代碼,展現了最多見的執行路徑:開發
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
函數就能夠爲對象分配內存空間了。
在對象的初始化過程當中除了使用 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
的所有內容。
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