問題:一個NSObject占用多少內(nèi)存?
首先我們創(chuàng)建一個NSObject對象
NSObject *obj = [[NSObject alloc] init];
其實這個問題想問的就是obj
這個對象所指的指針占用的內(nèi)存大小追驴。
如果我們想要搞清楚obj
對象所指指針的大小械哟,那么我們只要搞清楚NSObjcet
在內(nèi)存的布局及其底層相關(guān)的知識。
- 其實我們平時編寫的
Objective-C
代碼殿雪,底層實現(xiàn)其實都是C/C++
代碼
- 所以
OC
的面向?qū)ο蠖际腔?code>C/C++的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)的
說到這里暇咆,們我們來思考一個問題,Objective-C
的對象和類丙曙,主要基于C/C++
的什么數(shù)據(jù)結(jié)構(gòu)實現(xiàn)的呢爸业?
由于oc的類和對象可能包含很多種屬性如下代碼
{
int _age;
double _height;
NSString *_name;
}
所以根據(jù)上面的不同數(shù)據(jù)類型的屬性來猜測,那就是結(jié)構(gòu)體亏镰。
-
將
OC
代碼轉(zhuǎn)化成C/C++
代碼
那么我們就把OC
代碼嘗試著轉(zhuǎn)化成C/C++
代碼扯旷,來看一下到底是不是結(jié)構(gòu)體。
打開終端拆挥,將`OC `代碼轉(zhuǎn)化成`C/C++`
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 輸出的cpp文件
那么根據(jù)上面的終端指令薄霜,我們將OC
的main.m
文件轉(zhuǎn)化成C/C++
的main.cpp
文件,具體指令
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
指令輸入完成后回車纸兔,生成如下main.cpp
文件
-
NSObject
內(nèi)存本質(zhì)
將上一步的C++
代碼打開我們可以看一下NSObject
內(nèi)存本質(zhì)到底是什么樣子的
struct NSObject_IMPL {
Class isa;
};
有上面的代碼可以看出來惰瓜,NSObject
對象在內(nèi)存中就是一個結(jié)構(gòu)體
而其里面的Class isa,點進去我們可以看到他的定義
typedef struct objc_class *Class;
實際上,isa
就是一個指向結(jié)構(gòu)體的指針汉矿。
那么既然isa
是個指針崎坊,那么他在64位的環(huán)境下占8個字節(jié)
,在32環(huán)境上占4個字節(jié)
洲拇。(我們所使用的是64位架構(gòu)的)
因為這個結(jié)構(gòu)體就isa
1個成員奈揍,假設(shè)isa
的地址是0x100400b70
,那么這個結(jié)構(gòu)體的地址就應(yīng)該是isa
的地址赋续。所以obj
的地址應(yīng)該就是結(jié)構(gòu)體的地址男翰,這個地址占用的內(nèi)存大小就是結(jié)構(gòu)體的大小,即isa
的大小纽乱,isa
這個地址所占用的內(nèi)存大小為8個字節(jié)
蛾绎,那么NSObject
對象在內(nèi)存中所占用的大小也應(yīng)該是8個字
。這些就是NSObject
內(nèi)存本質(zhì)鸦列。
-
解決最上面的問題
根據(jù)上面NSObject
內(nèi)存本質(zhì)的分析租冠,我們應(yīng)該會認(rèn)為NSObject
對象在內(nèi)存中占用了8個字節(jié)
,那么實際上并不是薯嗤,而是16個字節(jié)
顽爹,為什么呢?讓我們來進一步分析:在runtime中有個class_getInstanceSize
方法獲取實例的大小骆姐,首先導(dǎo)入頭文件#import <objc/runtime.h>
镜粤,那么我們來打印一下看看
NSObject *obj = [[NSObject alloc] init];
NSLog(@"class_getInstanceSize--%zd", class_getInstanceSize([NSObject class]));
輸出結(jié)果為:
interview-OC對象的本質(zhì)[10809:700450] class_getInstanceSize--8
還有一個獲取內(nèi)存大小的方法捏题,導(dǎo)入頭文件#import <malloc/malloc.h>
NSObject *obj = [[NSObject alloc] init];
NSLog(@"malloc_size--%zd", malloc_size((__bridge const void *)obj));
輸出結(jié)果為:
interview-OC對象的本質(zhì)[10809:700450] malloc_size--16
根據(jù)上面的兩個方法獲取的內(nèi)存大小不一樣,class_getInstanceSize
獲取的大小為8個字節(jié)
繁仁,malloc_size
獲取的大小是16個字節(jié)
涉馅。為什么會出現(xiàn)兩種不同的情況呢,不要著急黄虱,我們來進一步分析
我們可以去runtime
的源碼里面稚矿,看一下class_getInstanceSize
具體是怎么實現(xiàn)的。OC所有開放的源碼地址https://opensource.apple.com/tarballs
我們找到runtime
源碼位置然后下載下來
點進去然后下載數(shù)字最大的捻浦。
下載完成晤揣,打開項目,然后找到
class_getInstanceSize
的實現(xiàn)然后我們點擊去看下alignedInstanceSize
實現(xiàn)
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
可以從注釋上看出來返回的是Class's ivar size
,類的成員變量的大小朱灿,
因為NSObject
對象只有一個isa
成員變量昧识,因為返回的是8個字節(jié)
我們還可以從源碼的另外一個角度來分析一下,看一下alloc
的時候分配了多大的內(nèi)存大小盗扒,我們還是搜索剛才的源碼allocWithZone
然后找到_objc_rootAllocWithZone
在這個方法中返回的是class_createInstance(cls, 0)
跪楞,然后跳轉(zhuǎn)進去,返回值再點擊去可以看到instanceSize
侣灶,再點進去可以看到如下的代碼
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
從上述代碼注釋中可以看到甸祭,CF
要求至少得返回16個字節(jié)
的內(nèi)存大小。
-
最終的答案
從上面的所有分析來看褥影,我們很容易能回答出文中最開始提出的問題
一個NSObject占用多少內(nèi)存池户?
答:
1、系統(tǒng)分配了16個字節(jié)給NSObject對象(可以通過malloc_size函數(shù)得到)
2凡怎、但NSObject對象內(nèi)部只使用了8個字節(jié)空間(在64bit環(huán)境下校焦,可以通過class_getInstanceSize函數(shù)獲得)