美文网首页
iOS OC对象创建本质

iOS OC对象创建本质

作者: 山杨 | 来源:发表于2021-10-21 16:42 被阅读0次

    OC的类本质是一个结构体
    通过对main.m转换main.cpp可以看出,类对象的本质就是一个结构体。

    @interface YSObject : NSObject
    @property (nonatomic, assign) NSUInteger age;
    @end
    
    struct NSObject_IMPL {
        Class isa;
    };
    struct YSObject_IMPL {
        struct NSObject_IMPL NSObject_IVARS;
        NSUInteger _age;
    };
    
    • 对象创建本质:
    YSObject *p = [[YSObject alloc] init];
    YSObject *p = ((YSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((YSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("YSObject"), sel_registerName("alloc")), sel_registerName("init"));
    去掉类型转换得到:
    YSObject *p = objc_msgSend(objc_msgSend(objc_getClass("YSObject"), sel_registerName("alloc")), sel_registerName("init"));
    

    可以看出创建的过程只是向YSObject类发送了sel_registerName("alloc")sel_registerName("init")消息,无法分析内存分配的过程。那么发送这两条消息后发生了什么呢?通过查看汇编代码(Debug->Debug workflow->Always Show Disassembly)发现调用了objc_alloc_init方法

    汇编代码.png

    Apple源码obj4中找到了objc_alloc_init函数(在NSObject.mm文件中)

    objc_alloc_init函数.png
    objc_alloc_init函数中调用了callAlloc函数
    callAlloc函数.png
    callAlloc中调用了_objc_rootAllocWithZone函数,全局搜索找到了它
    _objc_rootAllocWithZone函数.png
    _objc_rootAllocWithZone中调用了_class_createInstanceFromZone_class_createInstance
    _class_createInstanceFromZone和_class_createInstance.png
    _class_createInstanceFromZone中可以看出CF requires all objects be at least 16 bytes.CF要求所有的对象至少有16字节。通过calloc计算需要的内存大小
    malloc_zone_calloc.png

    把计算的bytes传给objc_constructInstance返回创建的实例对象

    objc_constructInstance

    • 在' bytes '所指向的位置创建一个' cls '的实例。
    • ' bytes '必须指向至少class_getInstanceSize(cls)的字节
    • 用0填充对齐内存。
    • 设置新对象的isa。调用任何c++构造函数。
    • 如果成功返回' bytes '。如果' cls '或' bytes '为空则返回nil
    • nil,或者如果c++构造函数失败。 objc_constructInstance.png
      至此,可以得出一个结论class_getInstanceSize(cls)得到的是创建cls需要的最小内存,而是实际上,64位 iOS系统分配的是16的整数倍的空间,例如:class_getInstanceSize(cls)的值为24,那么最终分配的空间大小为16*2=32。
      • malloc_size(const void *ptr)计算的是系统实际分配的内存
      • sizeof()是计算数据类型的大小,在编译的时候就已经得出了结果
      • 每一种数据类型都有自己的内存对齐方式
      @interface YSObject : NSObject
      {
          int height;
          int age;
      }
      @end
      
      需要的最小内存为4+4+8 = 16(int占4个字节,isa指针占8个字节),实际class_getInstanceSize([YSObject class])计算的结果也是16。再加入一个char类型数据
      @interface YSObject : NSObject
      {
          char _name;
          int height;
          int age;
      }
      @end
      
      需要的最小内存为4+4+1+8 = 17(char占1个字节),实际class_getInstanceSize([YSObject class])计算的结果却是24。简单来说就是在YSObject的实例对象创建的过程中做了内存对齐,而且class_getInstanceSize()的结果一定是8的倍数
      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() const {
         return word_align(unalignedInstanceSize());
      }
      // May be unaligned depending on class's ivars.
      // 根据类的成员变量不同,可能是未对齐的
      uint32_t unalignedInstanceSize() const {
         ASSERT(isRealized());
         return data()->ro()->instanceSize;
      }
      #ifdef __LP64__
      #   define WORD_SHIFT 3UL
      #   define WORD_MASK 7UL
      #   define WORD_BITS 64
      #else
      #   define WORD_SHIFT 2UL
      #   define WORD_MASK 3UL
      #   define WORD_BITS 32
      #endif
      // 7UL表示 7unsigned long
      static inline uint32_t word_align(uint32_t x) {
        return (x + WORD_MASK) & ~WORD_MASK;
      }
      

    calloc函数的实现在libmalloc源码

    calloc.png
    calloc中调用的_malloc_zonecalloc也是在libmalloc源码
    _malloc_zonecalloc.png
    • 最终找到kdebug_trace但是在libmalloc源码中没有找到源码(放在以后再说)
      MALLOC_TRACE.png

    相关文章

      网友评论

          本文标题:iOS OC对象创建本质

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