美文网首页日常收录
iOS 一个OC对象在内存中的布局&&占用多少内存

iOS 一个OC对象在内存中的布局&&占用多少内存

作者: 枫紫_6174 | 来源:发表于2020-09-08 14:58 被阅读0次

    一.先来看看我们平时接触的NSObject

    • NSObject *objc = [[NSObject alloc]init]的本质
      在内存中,这行代码就把objc转在底层实现中转成了一个结构体,其底层C++编译成结构体为:
    struct NSObject_IMPL {
        Class isa;
    };
    

    在64位机中,一个isa占8个字节,在32位机中,一个isa占4个字节(当然苹果后面的机型都是64位的,这里我们着重讲解64位机)

    • 我们先来看看这个创建好的objc占多少个字节
    int main(int argc, char * argv[]) {
        
        @autoreleasepool {
            // Setup code that might create autoreleased objects goes here.
            //定义一个objc
            NSObject *objc = [[NSObject alloc]init];
            //打印内存
            NSLog(@"tu-%zd",class_getInstanceSize([NSObject class]));
            NSLog(@"tu-%zd",malloc_size((__bridge const void *)(objc)));
        }
        
    }
    
    其打印结果为 objc打印结果
    • 为什么一个是8一个是16
      • 我们先来认识一下class_getInstanceSize、malloc_size的区别
        1.class_getInstanceSize:是一个函数(调用时需要开辟额外的内存空间),程序运行时才获取,计算的是类的大小(至少需要的大小)即实例对象的大小->结构体内存对齐
        2.创建的对象【至少】需要的内存大小不考虑malloc函数的话,内存对齐一般是以【8】对齐
        3.#import <objc/runtime.h>使用这个函数时倒入runtime运行时

      • malloc_size:堆空间【实际】分配给对象的内存大小 -系统内存对齐

        1. 在Mac、iOS中的malloc函数分配的内存大小总是【16】的倍数 即指针指向的内存大小
        2. import <malloc/malloc.h>使用时倒入这个框架
    • sizeof:是一个运算符,获取的是类型的大小(int、size_t、结构体、指针变量等),这些数值在程序编译时就转成常数,程序运行时是直接获取的

    看到上面对两个函数的认识,应该知道为什么输出的一个是8,一个是16了吧,当内存申请<16时,在底层分配的时候,系统会默认最低16个字节,系统给objc16个字节,而objc用到的是8个字节(没添加任何成员变量之前)

    二.内存对齐

    • 在上面的基础上我们新建一个类Student继承NSObject,那么对于student的底层C++编译实现就变成了:
    struct Student {
        struct NSObject_IMPL NSOBJECT_IVARS;
    };
    

    也就是说,继承关系,子类直接将父类的isa引用进来

    • 对于class_getInstanceSize(也就是类本质的内存对其)
      1.在student中创建成员变量:
    @interface Student : NSObject
    {
        @public
        int _age;
        int _no;
        int _tc;
    }
    @end
    

    其底层C++编译结构体就变成了

    struct Student {
        struct NSObject_IMPL NSOBJECT_IVARS;
        int _age;
        int _no;
        int _tc;
    };
    
    
    • 打印结果:
     //定义一个objc
            Student *objc = [[Student alloc]init];
            //打印内存
            NSLog(@"tu-%zd",class_getInstanceSize([Student class]));
            NSLog(@"tu-%zd",malloc_size((__bridge const void *)(objc)));
    

    2020-09-08 12:35:27.158568+0800 OC底层[1549:79836] tu-24
    2020-09-08 12:35:27.159046+0800 OC底层[1549:79836] tu-32

    • 先来说说24的由来
    由于创建对象的时候,内存是以8对齐,上面我们讲到一个对象里面包含了一个isa占8个字节,对于student来说它有四个成员变量,isa,age,no,tc,共占8+4+4+4=20字节,但是由于内存以8对齐的原因,我们看到的输出是24, 结构体8位对齐

    所以class_getInstanceSize在计算实例大小的时候就是24,其白色区域表示空出了四个字节

    • 再来看看32的由来
      上面我们说到malloc_size指的是实际堆分配的空间,它以16字节对齐


      堆内存对齐

    可以看到,空白的区域为空出了12个字节,总共为32个字节

    三.添加属性

    • 添加属性
    @interface Student : NSObject
    {
        @public
        int _age;
        int _no;
        int _tc;
    
    }
    @property (nonatomic, strong) NSString *name;
    @property (nonatomic, strong) NSArray *array;
    @end
    

    其在底层C++编译就变成了

    struct Student {
        struct NSObject_IMPL NSOBJECT_IVARS;
        int _age;
        int _no;
        int _tc;
        NSString _name;
        NSArray _array;
    };
    

    默认的会将属性生成的_name添加进结构体中,计算相应的大小

    总结:所以在实际计算类的占用空间大小的时候,根据添加的成员变量就可以计算出一个实例占用的内存大小(即计算出结构体的大小24,然后告诉系统,系统调用calloc分配内存的时候按照16对齐原则分配)

    相关文章

      网友评论

        本文标题:iOS 一个OC对象在内存中的布局&&占用多少内存

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