iOS OC对象创建本质

2021-10-21  本文已影响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
上一篇下一篇

猜你喜欢

热点阅读