美文网首页
对象实例的空间大小

对象实例的空间大小

作者: redye | 来源:发表于2020-01-08 19:29 被阅读0次

    我们在分析对象创建的流程时发现,对象在创建之前,第一步是先计算实例对象所占空间大小。所以我们今天来看一下是如何创建的。

    talk is cheap, show me the code!我们就从代码中寻找我们的答案。

    计算内存大小

    size_t size = cls->instanceSize(extraBytes);
     
    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;
    }
    

    首先,在获取对象实例空间大小时,要求所有的对象至少为 16 字节,为什么是 16 字节呢?

    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }
    
    static inline uint32_t word_align(size_t x) {
        return (x + WORD_MASK) & ~WORD_MASK;
    }
    
    #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
    

    其次,对象的地址进行了一个 字节对齐 的操作。在64位架构上进行 8 字节对齐,32位机器上进行4字节对齐。字节对齐这段代码只有一行:

    (x + WORD_MASK) & ~WORD_MASK;
    

    我们来分析下这行代码:

    WORD_MASK 是一个宏定义,那么就可以针对不同平台有不同的值,这里就是针对 64/32 位架构做了区分。

    (x + WORD_MASK) & ~WORD_MASK, 我们用一个例子来说明,以8字节对齐为例:

    uint32_t x = 25;
    uint32_t wordMask = 7
    uint32_t y = (x + wordMask) & ~wordMask;
    NSLog(@"%d", y); //32
    
    word_align.png

    我们在 lldb 里面将 xWORD_MASK 都转化成二进制计算:

        27      011011
    +
         7      000111
    =
        31      100010
    &  
        ~7      111000
    =
        32      100000      
    

    字节对齐的操作之后,对象的空间大小是8的倍数,那么对象的起始地址也必定是8的倍数。

    还有另外一种方法也能达到8字节对齐的效果,即

    (x + WORD_MASK) >> 3 << 3
    

    如果要符合不同平台,需要定义个宏定义,其实上面的宏定义里面已经定义好了,即

    (x + WORD_MASK) >> WORD_SHIFT << WORD_SHIFT
    

    这是一个例子

    现在我们给出我们的一个类 SMPerson,计算一下他所占的内存空间:

    @interface SMPerson : NSObject
    
    @property (nonatomic, copy) NSString *name;      // 8 bytes
    @property (nonatomic, copy) NSString *address;   // 8 bytes
    @property (nonatomic, assign) int age;           // 4 bytes
    @property (nonatomic, assign) CGFloat height;    // 8 bytes
    @property (nonatomic, assign) char ch;           // 1 byte
    
    @end
    

    OC 类在底层实现还有一个隐藏的 isa 指针,这里如果不考虑成员变量在内存中是如何分布(至于这里为什么不需要考虑内存分布,是因为这部分编译器已经帮我们处理好了),计算出来的实例对象的大小为40字节,下面我们验证一下:

    在 main.m 文件中

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            SMPerson *person = [[SMPerson alloc] init];
            person.name = @"CC";
            person.age = 18;
            person.address = @"上海";
            person.height = 180;
            NSLog(@"%lu", class_getInstanceSize([person class]));
        }
        return 0;
    }
    

    控制台输出:

    2019-12-22 23:26:50.541712+0800 objc-debug[13935:27611066] 40
    

    是不是感觉自己棒棒哒 (๑•̀ㅂ•́)و✧

    相关文章

      网友评论

          本文标题:对象实例的空间大小

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