OC對象(一)-- alloc和init底層到底在干嘛
OC對象(二)-- 內(nèi)存對齊和calloc中的16字節(jié)對齊
OC對象(三)-- isa結(jié)構(gòu)分析
本文使用的源碼是objc4-787.1
init
看看init源碼:
- (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;
}
init方法中調(diào)用了_objc_rootInit方法淌实,最后返回的就是self粮彤。所以可以說init中什么都沒有做肝劲。
用個demo驗證一下:
DZObject *obj1 = [DZObject alloc];
DZObject *p1 = [obj1 init];
DZObject *p2 = [obj1 init];
NSLog(@"%@ - %@ - %@", obj1, p1, p2);
NSLog(@"%p - %p - %p", obj1, p1, p2);
NSLog(@"%p - %p - %p", &obj1, &p1, &p2);
說明:
- DZObject是自定義的類愤炸,繼承自NSObject。
- alloc一個實例obj1
- 再分別定義實例p1和p2战惊,通過obj1調(diào)用init方法
- 分別打印實例對象溯职、指針、和指針地址
打印結(jié)果如圖:
對象和指針都是相同的胳施,指針地址不同。內(nèi)存中結(jié)構(gòu)如圖:
init方法是蘋果提供的工廠方法肢专,對實例對象沒有進行任何處理巾乳。開發(fā)者可以自定義init的實現(xiàn),比如初始化一些屬性數(shù)據(jù)的工作鸟召。
alloc
alloc流程圖
圖中粉色部分是alloc的核心部分,主要進行了三部操作
- cls->instanceSize:計算需要開辟的內(nèi)存空間氨鹏,內(nèi)部實現(xiàn)是個算法欧募,在最新源碼的算法是16字節(jié)對齊。
- calloc:用計算好的size作為參數(shù)仆抵,調(diào)用calloc方法跟继,開辟內(nèi)存空間。
- obj->initInstanceIsa:初始化示例的isa
instanceSize實現(xiàn)(字節(jié)對齊算法)
函數(shù)方法調(diào)用流程圖入下:
核心代碼如下:
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
計算過程示例:例如x=4镣丑,如下表:
16進制 | 2進制 |
---|---|
x=4 | 0000 0100 |
+ | |
15 | 0000 1111 |
= | |
19 | 0001 0011 |
& | |
~15 | 1111 0000 |
16 | 0001 0000 |
經(jīng)過算法后舔糖,得到的結(jié)果都是16的倍數(shù)(可以使用大于16的數(shù)字算一遍)。這就是16字節(jié)對齊莺匠。
new
經(jīng)常會用new方法直接示例對象金吗,例如
DZObject *obj = [DZObject new];
看看new的底層源碼實現(xiàn):
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
通過源碼可以看到,new做了alloc和init合并的事趣竣。==但是不建議直接使用new摇庙。如果一個自定義類中添加了initWithXXX:方法,方法中做了初始化的相關(guān)邏輯遥缕。那么直接調(diào)用new卫袒,只是調(diào)用了系統(tǒng)提供的init方法。==
NSObject alloc
當用NSObject alloc的時候单匣,走的流程與上面的一樣夕凝。其實alloc的真正調(diào)用的是objc_alloc方法,驗證方式:在alloc下斷點户秤,打開匯編码秉,運行:
可以看到匯編代碼調(diào)用的是objc_alloc,接下來看看它的源碼實現(xiàn):
// Calls [cls alloc].
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
因此alloc的真正的流程圖是這樣的:
擴展 - 那為什么調(diào)用alloc方法時會先調(diào)用objc_alloc函數(shù)呢虎忌?
蘋果使用LLVM編譯工具泡徙,在編譯期間對alloc方法進行了特殊處理,可以理解為是系統(tǒng)級別的hook方法膜蠢,也就是將alloc方法和objc_alloc函數(shù)進行交換堪藐。
展示一下部分llvm源碼中tryGenerateSpecializedMessageSend
函數(shù)中的部分實現(xiàn):
再來看看tryGenerateSpecializedMessageSend
的調(diào)用地方:
大致的思路是莉兰,系統(tǒng)將alloc標記為特殊消息發(fā)送。一個類調(diào)用alloc的時候系統(tǒng)會去調(diào)用objc_alloc礁竞。在objc_alloc源碼中通過objc_msgSend再次發(fā)送一次alloc消息(這次不會被系統(tǒng)標記成特殊消息糖荒,正常調(diào)用alloc方法)。