您的位置:首页 > 移动开发 > IOS开发

iOS OC 对象原理探索二

2020-04-20 15:02 961 查看

iOS OC 对象原理探索二

  • 成员变量内存探究
  • 上一篇提到alloc开辟内存是8字节对齐,用空间换取查找时间,那么像

    int
    char
    这样非8字节的变量,在内存中是什么样的呢?

    首先我们看看对齐原则如下:

    内存对齐原则

    1. 数据成员对齐规则:

    结构体(struct)或(联合体union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的
    起始位置要从该成员大小或者成员的子成员大小的整数倍开始(只有该成员有子成员,比如数组、结构体等),
    例如:int为4字节,则要从4的整数倍地址开始存储

    2. 结构体作为成员:

    如果一个结构体里有某些结构体成员,则结构体成员要从其中内部最大元素大小的整数倍地址开始存储.
    例:(struct a 中存有 struct b, b中有char,int,double等元素,那么b应该从8的整数倍开始存储)

    3. 收尾工作:

    结构体的总大小,也就是sizeof的结构,必须是起内部最大的成员整数倍,不足的要补齐

    接下来看一下在

    结构体
    结构体
    嵌套中每个变量所占内存的分析,分析代码如下:

    struct LGStruct1 {
    char a;      // 1 + 7  min(0 1)  0
    double b;    // 8	   min(1 8)  (1-7填充) 8 9 10 11 12 13 14 15
    short d;     // 2 + 2  min(16 2) 16 17
    int c;       // 4      min(18 4) (18 19填充) 20 21 22 23 -- 24
    } MyStruct1;
    
    struct LGStruct2 {
    
    double b;    // 8  0-7
    int c;       // 4  min(8 4)  8 9 10 11
    char a;      // 1  min(12 1) 12
    short d;     // 2  min(13 2) (13填充) 14 15  -- 16
    struct LGStruct1 r;  // min(16 24) struct里最大double 8的倍数
    
    } MyStruct2;  // struct里最大double 8,从8的倍数地址开始存储
    // NSLog(@"%lu-%lu",sizeof(MyStruct1),sizeof(MyStruct2));
    // 输出  24-40
    struct LGStruct3 {
    
    double b;   // 8  0-7
    int c;      // 4  min(8 4)  8 9 10 11
    char a;     // 1  min(12 1) 12
    struct LGStruct1 r;  // min(13 24) (13 14 15)
    // 16 - 39 struct里最大double 8的倍数
    short d;    // 2  min(40 2)  40 41  -- 48
    } MyStruct3;  // struct里最大double 8 补齐为48
    // 结构体的总大小,也就是sizeof的结构,必须是起内部最大的成员整数倍,不足的要补齐// 输出  24-48
    

    根据上面代码打印分析,我们很容易分析出

    结构体struct
    结构体struct
    嵌套所占的内存,完全遵守内存对齐原则。

    成员变量内存探究

    上一篇文章我们分析alloc源码得知,对象开辟内存空间是8的倍数(8字节对齐),最少占用16字节(16 - 8 > 16),那么系统真的会按照我们的意愿开辟内存么?下面我们来分析一下:

    1.代码分析

    LGTeacher  *p = [[LGTeacher alloc] init];
    p.name = @"Cooci";
    p.age  = 18;
    p.height = 185;
    p.hobby  = @"女";
    p.sex    = 2;
    p.ch1    = 'a';
    p.ch2    = 'b';

    打印分析如下:


    由此可以看出,对象的属性并未按照每个属性8字节来开辟内存的,而是按照开篇所说的内存对齐原则来分配的,那么我们分析一下代码:

    LGTeacher  *p = [LGTeacher alloc];
    // isa ---- 8
    p.name = @"LG_Cooci";   // 8
    p.age  = 18;            // 4
    p.height = 185;         // 8
    p.hobby  = @"女";       // 8
    NSLog(@"%lu - %lu",class_getInstanceSize([p class]),malloc_size((__bridge const void *)(p)));

    应该占40个字节,那么实际系统开辟的内存打印如下:

    实际开辟了48内存,对象申请的内存的大小 和 系统开辟的大小并 不一致,接下来我们分析一下

    calloc
    源码分析,看一下系统是怎么开辟内存的

    2.calloc源码分析

    通过源码分析,流程如下:

    关键代码:

    static MALLOC_INLINE size_t
    segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
    {
    // size = 40
    size_t k, slot_bytes;
    
    if (0 == size) {
    size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
    }
    // 40 + 16-1 >> 4 << 4
    // 40 - 16*3 = 48
    
    //
    // 16
    k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quanta
    slot_bytes = k << SHIFT_NANO_QUANTUM;							// multiply by power of two quanta size
    *pKey = k - 1;													// Zero-based!
    
    return slot_bytes;
    }

    由此看出:

    对象开辟内存是16字节对齐,是参照整个当前对象;前面8字节对齐,是参照对象里面的属性。
    16字节对齐,系统开辟内存,避免风险,防止内存溢出,属性8字节内存对齐也有一定多余的空间  不确定属性具体排列顺序,不确定末尾是否有
    • 点赞
    • 收藏
    • 分享
    • 文章举报
    亮亮不想说话 发布了18 篇原创文章 · 获赞 10 · 访问量 273 私信 关注
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: