(一)OC對象在底層中的布局
我們平時編寫的Objective-C
代碼,在底層都是使用C/C++
實現。
即Objective-C
-> C/C++
-> 匯編語言
-> 機器語言
。
我們定義個NSObject
對象
NSObject *object = [[NSObject alloc] init];
使用終端命令將OC
代碼轉換成iOS能支持的C/C++
代碼;
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 'OC源文件' -o '輸出的CPP文件'
通過閱讀轉換后的.cpp
文件,可以找到一個名為 NSObject_IMPL
的結構體。分析結構體名字實際含義是 NSObject Implementation
struct NSObject_IMPL {
Class isa;
};
因此我們可以知道OC對象在底層的布局是一個結構體
。
(二)對象在內存中的占用大小
引入<objc/runtime.h>
, 使用 class_getInstanceSize
方法可獲取NSObject
實例對象的 成員變量
所占用的大小,可以觀察到返回 8 個字節
class_getInstanceSize([NSObject class])
引入<malloc/malloc.h>
, 使用malloc_size
方法獲取指針所指向的內存大小
,可以觀察到返回16
個字節。
malloc_size((__bridge const void *)(object));
通過閱讀Objc源碼 可得知在alloc
一個對象時,CoreFunction
框架硬性規定分配內存空間不小于16
個字節
總結:
系統分配了
16
個字節給NSObject
對象(通過malloc_size
函數獲得),但NSObjec
t對象內部只使用了8
個字節的空間(64bit環境下,可以通過class_getInstanceSize
函數獲得)
(三)通過XCode工具分析內存
首先打印NSObject
對象地址,通過工具ViewMemory
查看。
可觀察到前16
個字節中,只有8
個字節存在數據(存放isa
指針)
(四)自定義類內存分析
定義Student
類,初始化。通過重復之前步驟,可觀察到底層實現。
struct Student_IMPL {
Class isa;
int _no;
int _age;
};
@interface Student : NSObject
{
@public
int _no;
int _age;
}
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *stu = [[Student alloc] init];
stu->_no = 4;
stu->_age = 5;
NSLog(@"%zd", class_getInstanceSize([Student class]));
NSLog(@"%zd", malloc_size((__bridge const void *)stu));
}
return 0;
}
通過使用class_getInstanceSize
與malloc_size
方法可觀察到占用16
個字節存儲空間。
使用
ViewMemory
查看前8
個字節存放isa
,_no
與_age
分別占用4
個字節。
我們也可以使用LLDB
指令打印相關地址信息
,并且可使用memory write
指令直接修改內存中的數據
可以觀察到將
_no
中的值修改為了8
。
(五)繼承類的內存占用分析
@interface Person : NSObject
{
@public
int _age;
}
@end
@implementation Person
@end
@interface Student : Person
{
int _no;
}
@end
@implementation Student
@end
分析:Person
類中isa
指針占用8
個字節,_age
占用4
個字節。通過CoreFunction
框架硬性規定分配內存空間不小于16個字節
。得知共占用16
個字節。
但是
即使無此硬性規定,通過內存對齊
規則:結構體的最終大小,必須是最大成員的倍數。
。
Student
類占用16
個字節。isa
占用8
個字節,_age
占用4
個字節,剩余4
個字節,被_no
占用。