objc源碼中NSObject如何進(jìn)行初始化
推薦 + 挑錯(cuò) + 收藏(0) + 用戶評(píng)論(0)
+ alloc 和 - init 這一對(duì)我們?cè)?iOS 開發(fā)中每天都要用到的初始化方法一直困擾著我, 于是筆者仔細(xì)研究了一下 objc 源碼中 NSObject 如何進(jìn)行初始化。
在具體分析對(duì)象的初始化過程之前,我想先放出結(jié)論,以免文章中的細(xì)枝末節(jié)對(duì)讀者的理解有所影響;整個(gè)對(duì)象的初始化過程其實(shí)只是 為一個(gè)分配內(nèi)存空間,并且初始化 isa_t 結(jié)構(gòu)體的過程。
alloc 方法分析
先來看一下 + alloc 方法的調(diào)用棧(在調(diào)用棧中省略了很多不必要的方法的調(diào)用):
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)
這個(gè)調(diào)用棧中的方法涉及了多個(gè)文件中的代碼,在下面的章節(jié)中會(huì)對(duì)調(diào)用的方法逐步進(jìn)行分析,如果這個(gè)調(diào)用棧讓你覺得很頭疼,也不是什么問題。
alloc 的實(shí)現(xiàn)
+ (id)alloc {
return _objc_rootAlloc(self);
}
alloc 方法的實(shí)現(xiàn)真的是非常的簡單, 它直接調(diào)用了另一個(gè)私有方法 id _objc_rootAlloc(Class cls)
id _objc_rootAlloc(Class cls) {
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
這就是上帝類 NSObject 對(duì) callAlloc 的實(shí)現(xiàn),我們省略了非常多的代碼,展示了最常見的執(zhí)行路徑:
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);
}
對(duì)象初始化中最重要的操作都在 _class_createInstanceFromZone 方法中執(zhí)行:
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;
}
對(duì)象的大小
在使用 calloc 為對(duì)象分配一塊內(nèi)存空間之前,我們要先獲取對(duì)象在內(nèi)存的大?。?/p>
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;
}
實(shí)例大小 instanceSize 會(huì)存儲(chǔ)在類的 isa_t 結(jié)構(gòu)體中,然后經(jīng)過對(duì)齊最后返回。
Core Foundation 需要所有的對(duì)象的大小都必須大于或等于 16 字節(jié)。
在獲取對(duì)象大小之后,直接調(diào)用 calloc 函數(shù)就可以為對(duì)象分配內(nèi)存空間了。
isa 的初始化
在對(duì)象的初始化過程中除了使用 calloc 來分配內(nèi)存之外,還需要根據(jù)類初始化 isa_t 結(jié)構(gòu)體:
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;
}
}
上面的代碼只是對(duì) isa_t 結(jié)構(gòu)體進(jìn)行初始化而已:
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;
};
};
init 方法
NSObject 的 - init 方法只是調(diào)用了 _objc_rootInit 并返回了當(dāng)前對(duì)象:
- (id)init {
return _objc_rootInit(self);
}
id _objc_rootInit(id obj) {
return obj;
}
非常好我支持^.^
(0) 0%
不好我反對(duì)
(0) 0%
下載地址
objc源碼中NSObject如何進(jìn)行初始化下載
相關(guān)電子資料下載
- iOS17.1可能明天發(fā)布,iOS17.1主要修復(fù)哪些問題? 377
- 華為全新鴻蒙蓄勢(shì)待發(fā) 僅支持鴻蒙內(nèi)核和鴻蒙系統(tǒng)應(yīng)用 719
- 蘋果手機(jī)系統(tǒng)iOS 17遭用戶質(zhì)疑 731
- iPhone12輻射超標(biāo)?蘋果推送iOS 17.1解決此事 750
- 傳華為囤積零部件 目標(biāo)明年智能手機(jī)出貨7000萬部;消息稱 MiOS 僅限國內(nèi),小米 28208
- 蘋果推送iOS17.0.3,解決iPhone15Pro系列存在機(jī)身過熱 216
- Testin云測(cè)兼容和真機(jī)服務(wù)平臺(tái)中上線iPhone 15系列手機(jī) 208
- 利爾達(dá)推出搭載HooRiiOS的Matter模組 145
- 運(yùn)放參數(shù)解析:輸入偏置電流(Ibias)和失調(diào)電流(Ios) 128
- 昆侖太科發(fā)布支持國產(chǎn)飛騰騰銳D2000芯片的開源BIOS固件版本 448