美文网首页
OC对象在runtime时期开辟的的内存大小

OC对象在runtime时期开辟的的内存大小

作者: 无极战思 | 来源:发表于2020-06-15 11:40 被阅读0次

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 ;
 }
  1. 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个字节, 依此类推

相关文章

网友评论

      本文标题:OC对象在runtime时期开辟的的内存大小

      本文链接:https://www.haomeiwen.com/subject/nqecxktx.html