1.创建一个继承于NSObject的Dog类,调用 class_getInstanceSize 和 malloc_size 方法
Dog*dogSizeTest=[[Dog alloc]init];
NSLog(@"dogSizeTest对象实际需要的内存大小:%zd",class_getInstanceSize([dogSizeTest class]));
NSLog(@"dogSizeTest对象实际分配内存大小:%zd",malloc_size((__bridge const void *) (dogSizeTest)));
2.输出:
2020-06-15 11:20:00.207375+0800 ClangTest[1963:103537] dogSizeTest对象实际需要的内存大小:8
2020-06-15 11:20:00.207808+0800 ClangTest[1963:103537] dogSizeTest对象实际分配内存大小:16
3.可以看出系统开辟了16个字节, 但是该对象只是用了8个字节,这是为什么了?
这是因为NSObject 对象真正的是一个结构体指针,所以占用了8个字节
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
@interface NSObject <NSObject> {
Class isa ;
}
- 在 https://opensource.apple.com/tarballs/找到 runtime 源码, 搜索 class_getInstanceSize 可以找到下面的代码, 可以看出 class_getInstanceSize 获取的是类的成员变量的 size, 并不是一个对象所占用的空间
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
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;
}
5.那为什么会分配16个字节了? 也是在 runtime 里找到了答案, 当 size < 16 时, 会默认分配16个字节
6.在Dog类里面增加一个属性
@interface Dog : NSObject
@property(nonatomic,assign)int age;
7.重新输出:
2020-06-15 11:43:49.687251+0800 ClangTest[2109:119071] dogSizeTest对象实际需要的内存大小:16
2020-06-15 11:43:49.687679+0800 ClangTest[2109:119071] dogSizeTest对象实际分配内存大小:16
理论上讲我们增加了一个int 类型的age 应该增加4个字节 打印出来的大小应该是12 但为什么是16 ?看runtime中的代码
OC的对齐函数
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
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;
}
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
主要来看 (x + WORD_MASK) & ~WORD_MASK; 这个结果 把宏定义转过来就是 (x+7)& ~7 就拿刚才的结果举例就变成了(12+7) & ~7转成二进制就是(00001100 + 00000111) & 11111000就转成 00010011 & 11111000结果就是 00010000 就是 16,
再来看class_getInstanceSize
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
这时自定义对象包含了
1个结构体指针 (8字节)
1个 int _age (4字节)
8 + 4 = 12 字节
没有添加任何成员变量时, 只有1个结构体指针, 所以会是8字节
添加1个成员变量时, 有1个结构体指针, 和1个 int 类型的值, 那就是 8 + 4 = 12, 为什么 class_getInstanceSize 得到的结果是16了. 其实扩容基数是8(内存对齐原因, 结构体的大小必须是最大成员大小的倍数), 8个字节不够了, 就开辟了8*2个字节, 依此类推
网友评论