美文网首页专注iOS开发的小渣渣
OC底层探索05-内存对齐

OC底层探索05-内存对齐

作者: Henry________ | 来源:发表于2020-09-12 19:04 被阅读0次
    上篇对对象实际内存占用和内存分配计算做了解释,但是留了一个小坑:对象属性的大小计算。

    众所周知对象最终是以结构体的形式存在,所以通过对结构体的大小的探索来延伸到对象.

    内存对齐

    普通结构体,例1:

    struct HRStruct {
        int c;  //4
        double a;   //8
        char b; //1
        char d;    //1
    }HRStruct;
    
    NSLog(@"HRStruct结构体大小: %lu",sizeof(HRStruct));
    
    • 直接计算: 4 + 8 + 1 + 1 = 14

    以下是输出结果:


    为什么会这样呢,带着问题我们继续。

    普通结构体,例2

    struct HRStruct {
        double a;   //8
        int c;  //4
        char b; //1
        char d;    //1
    }HRStruct;
    
    • 直接计算: 8 + 4 + 1 + 1 = 14

    仔细观察这个结构体中的变量是相同的,不同的只是顺序.
    以下是输出结果:


    得出一个结论:根据顺序不同也会造成所占内存大小不同,可是为什么会这样呢?我们继续。

    结构体嵌套,例3

    struct HRStruct {
        double a;   //8
        int c;  //4
        char b; //1
        char d;    //1
    }HRStruct;
    
    struct HRStruct2 {
        double a;   //8
        int c;  //4
        char b; //1
        struct HRStruct str;    // 16
    }HRStruct2;
    
    • 直接计算: 8 + 4 + 1 + 16 = 29

    以下是输出结果:


    内存的计算并不是简单的加法,而是做了一定的调整.

    内存对齐原则

    内存的计算是按照以下规则:

    • 【原则一】 数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要
      从该成员大小或者成员的子成员大小的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储
    • 【原则二】收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补⻬。
    • 【原则三】结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b
      里有char,int ,double等元素,那b应该从8的整数倍开始存储.),结构体作为成员也需要按最大成员的整数倍进行补⻬。

    例子验证

    各种类型大小,方便计算时查看
    例1:
    struct HRStruct {
        int c;  //4
        double a;   //8
        char b; //1
        char d;    //1
    }HRStruct;
    
    • 通过内存布局图,直观的查看内存占用情况


    例2:
    struct HRStruct {
        double a;   //8
        int c;  //4
        char b; //1
        char d;    //1
    }HRStruct;
    
    • 通过内存布局图,直观的查看内存占用情况


    例3:
    struct HRStruct {
        double a;   //8
        int c;  //4
        char b; //1
        char d;    //1
    }HRStruct;
    
    struct HRStruct2 {
        double a;   //8
        int c;  //4
        char b; //1
        struct HRStruct str;    // 16
    }HRStruct2;
    
    • 通过内存布局图,直观的查看内存占用情况!


    内存优化(属性重排)

    通过例1、例2的对比,我们得出一个结论:根据顺序不同也会造成所占内存大小不同。所以通过调整的成员顺序可以优化内存大小、优化内存的布局,从而提高内存的读取效率.

    • 如果是结构体中数据成员是根据内存从小到大的顺序定义的,根据内存对齐规则来计算结构体内存大小,需要增加有较大的内存padding即内存占位符,才能满足内存对齐规则,比较浪费内存
    • 如果是结构体中数据成员是根据内存从大到小的顺序定义的,根据内存对齐规则来计算结构体内存大小,我们只需要补齐少量内存padding即可满足堆存对齐规则,这种方式就是苹果中采用的,利用空间换时间,将类中的属性进行重排,来达到优化内存的目的

    我们能想到这个点,苹果的开发工程师一定也想到了这个点。

    我们通过一个对象的内存情况来验证一下我们的猜测:

    @interface HRTest : NSObject
            @property(nonatomic, copy)NSString *name;
            @property(nonatomic, copy)NSString *hobby;
            @property(nonatomic, assign)double height;
            @property(nonatomic, assign)char a;
            @property(nonatomic, assign)char ab;
    @end
    
            HRTest * t = [HRTest alloc];
            t.name = @"Henry";
            t.hobby = @"woman";
            t.height = 180.0;
            t.a = 'a';
            t.ab = 'A';
    

    打印结果:


    • name、hobby是变量的第一个、第两个属性,为什么却存在第三、第四个指针中呢?
    第一个指针:isa指针
    第二个指针:短类型重排

    直接打印是乱码,但是仔细看还是有一点线索的。

    • 重点是为什么第二个指针里存的最后两个char类型的变量,这里就是apple对对象进行了属性重排,也就是内存对齐,验证了之前我们的猜测。

    结构体是不进行属性重排的,只有对象才会进行属性重排

    第五个指针:double类型

    apple对double,float进行了优化并不是直接存储。

    验证一下:


    总结

    apple通过内存对齐将对象的内存进行了极致的压榨,这一点提现在很多地方,真的值得我们学习。

    相关文章

      网友评论

        本文标题:OC底层探索05-内存对齐

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