(一)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函数获得),但NSObject对象内部只使用了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占用。