問題:一個NSObject占用多少內(nèi)存?
首先我們創(chuàng)建一個NSObject對象
NSObject *obj = [[NSObject alloc] init];
其實(shí)這個問題想問的就是obj這個對象所指的指針占用的內(nèi)存大小。
如果我們想要搞清楚obj對象所指指針的大小,那么我們只要搞清楚NSObjcet在內(nèi)存的布局及其底層相關(guān)的知識。
- 其實(shí)我們平時編寫的
Objective-C代碼,底層實(shí)現(xiàn)其實(shí)都是C/C++代碼
OC代碼的實(shí)現(xiàn) - 所以
OC的面向?qū)ο蠖际腔?code>C/C++的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的
說到這里,們我們來思考一個問題,Objective-C的對象和類,主要基于C/C++的什么數(shù)據(jù)結(jié)構(gòu)實(shí)現(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,點(diǎn)進(jìn)去我們可以看到他的定義
typedef struct objc_class *Class;
實(shí)際上,isa就是一個指向結(jié)構(gòu)體的指針。
那么既然isa是個指針,那么他在64位的環(huán)境下占8個字節(jié),在32環(huán)境上占4個字節(jié)。(我們所使用的是64位架構(gòu)的)
因?yàn)檫@個結(jié)構(gòu)體就isa1個成員,假設(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é),那么實(shí)際上并不是,而是16個字節(jié),為什么呢?讓我們來進(jìn)一步分析:在runtime中有個class_getInstanceSize方法獲取實(shí)例的大小,首先導(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)兩種不同的情況呢,不要著急,我們來進(jìn)一步分析
我們可以去runtime的源碼里面,看一下class_getInstanceSize具體是怎么實(shí)現(xiàn)的。OC所有開放的源碼地址https://opensource.apple.com/tarballs
我們找到runtime源碼位置然后下載下來

點(diǎn)進(jìn)去然后下載數(shù)字最大的。
下載完成,打開項(xiàng)目,然后找到
class_getInstanceSize的實(shí)現(xiàn)
然后我們點(diǎn)擊去看下alignedInstanceSize實(shí)現(xiàn)
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
可以從注釋上看出來返回的是Class's ivar size,類的成員變量的大小,
因?yàn)?code>NSObject對象只有一個isa成員變量,因?yàn)榉祷氐氖?code>8個字節(jié)
我們還可以從源碼的另外一個角度來分析一下,看一下alloc的時候分配了多大的內(nèi)存大小,我們還是搜索剛才的源碼allocWithZone然后找到_objc_rootAllocWithZone在這個方法中返回的是class_createInstance(cls, 0),然后跳轉(zhuǎn)進(jìn)去,返回值再點(diǎn)擊去可以看到instanceSize,再點(diǎn)進(jìn)去可以看到如下的代碼
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ù)獲得)
