作為一名軟件開發(fā)工程師袭蝗,我們都了解,程序最終要運行在終端般婆,需要經(jīng)過預處理到腥、編譯、裝載蔚袍、鏈接幾個過程乡范。正因如此,想了解NSObject的本質(zhì)問題啤咽,我們就需要知道在編譯階段都做了些什么事情晋辆。
Object-C語言屬于C語言分支,很自然我們會想到闰蚕,它的編譯過程會不會是先轉(zhuǎn)換成C/C++,然后再轉(zhuǎn)換為匯編語言连舍,繼而形成最終的機器碼010101没陡,經(jīng)過鏈接后,開始真正的運行起來索赏。如圖所示:
那么怎么來進行驗證呢盼玄?首先我們新建一個命令行工程,創(chuàng)建一個NSObject對象:
????NSObject *obj = [[NSObject alloc]init];
利用Clang編譯器前端潜腻,將我們所編寫的Object-C 語言轉(zhuǎn)換為C++埃儿。
打開終端,在終端輸入命令:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.arm64.cpp
該條指令的意思是利用 Xcode 的clang編譯器融涣,將main.m文件童番,編譯為支持iOS 64位架構(gòu)的C++文件精钮,導出文件名為main.arm64.cpp。
打開生成的C++文件剃斧,搜索 NSObject_Impl轨香,我們會看到如下代碼:
看名字:NSObject_IMPL,我們可以理解為NSObject_implection幼东,及NSObject類的實現(xiàn)部分臂容,對比NSObject.h中對于NSObject的定義:
此時我們可以發(fā)現(xiàn),@interface 類經(jīng)編譯器編譯后根蟹,變成了C++中的結(jié)構(gòu)體類型脓杉,也就是說NSObject的本質(zhì)就是C++中的結(jié)構(gòu)體。
明白了NSObject是通過C++的結(jié)構(gòu)體進行實現(xiàn)的简逮,下一步的工作就是探究一個NSObject對象球散,在內(nèi)存中占用多少內(nèi)存了。
結(jié)構(gòu)體中只有一個Class類型的isa成員买决,那么Class又是什么結(jié)構(gòu)呢沛婴,繼續(xù)深究我們發(fā)現(xiàn):
ISA其實是一個指向結(jié)構(gòu)體的指針,在64位架構(gòu)中督赤,一個指針占有8個字節(jié)的內(nèi)存大小嘁灯,因為結(jié)構(gòu)體NSObject中只有一個成員isa,也就是說ISA的地址就是NSObject的地址躲舌,NSObject占用8個字節(jié)的存儲空間丑婿。為了驗證我們的猜測是否正確,可以通過runtime中提供的class_getInstanceSize來進行驗證:
結(jié)果和我們猜測的一樣没卸,輸出size = 8羹奉。
細心的朋友已經(jīng)發(fā)行在控制臺中還有一個打印結(jié)果 allocSize = 16。
那這個又該如何解釋呢约计?
我們通過runtime獲取的是實際占用的空間大小诀拭,通過malloc_size函數(shù)獲取的是系統(tǒng)分配的內(nèi)存大小,也就是說系統(tǒng)給NSObject對象分配了16個字節(jié)的空間煤蚌,但是結(jié)構(gòu)體中只有一個isa成員耕挨,在64位環(huán)境下,只占用了前面8個字節(jié)大小尉桩。
同時我們也可以利用Xcode的debug工具來進行驗證筒占。
在剛才的斷點處,我們看到NSObject的地址為0x100402f90蜘犁,打開Xcode的viewMemory調(diào)試器翰苫,步驟為Debug->DebugWorkFlow->ViewMemory,在address欄輸入剛才的地址,看到如下結(jié)果:
注意:我們在這里看到左邊的地址是十進制表示形式奏窑,右邊顯示的是16進制表示法导披,0x100402f90對應的十進制值為:
系統(tǒng)分配了一段連續(xù)的內(nèi)存空間,1個16進制數(shù)字代表4個2進制位良哲,2個16進制剛好表示1個字節(jié)盛卡。從圖中我們可以看出前8個字節(jié)已經(jīng)被占用,也就是isa變量的地址筑凫,后8個字節(jié)為0滑沧,為空。從這里也可以間接證明系統(tǒng)為NSObject對象分配了16個字節(jié)巍实,實際只用了8個字節(jié)的內(nèi)存大小滓技。
總結(jié):1.NSObject對象的本質(zhì)是結(jié)構(gòu)體
? ? ? ? ? ? 2.在64位環(huán)境下,系統(tǒng)為NSObject對象分配了16個字節(jié)棚潦,因為結(jié)構(gòu)體中只有一個指向結(jié)構(gòu)體的指針isa成員令漂,只占用了8個字節(jié)的大小。
? ? ? ? ? ? 3.通過runtime中的?class_getInstanceSize() 函數(shù)獲取到的是對象實際占用的地址
? ? ? ? ? ? ? ?通過maclloc函數(shù):malloc_size()獲取到的是系統(tǒng)為對象分配的地址丸边。
如果還有不清楚的地方叠必,歡迎探討。
參考內(nèi)容:小碼哥底層原理:NSObject對象的本質(zhì)