ios对象的底层探索(下)

481 阅读4分钟

0a000581fca74098bc53b876f5e957ae.jpeg

我们在上一篇文章中已经初步了解了,alloc是如何分配内存等,结构体按最内存对齐,oc类是16字节对齐等内容。

  1. 影响对象内存的因素

对象⾥⾯存储了⼀个isa指针 + 成员变量的值,isa指针是固定的占8个字节,所以影响对象内存的只有成员变量(属性会⾃动⽣成带下划线的成员变量)

image.png

image.png

image.png 我们验证看到,对象P并未赋值。但是p的内存占用了48字节(8+8+4+8+2+1=31 按8对齐40+isa8)

  1. 对象的内存分布

在对象的内部是以8字节进⾏对⻬的。 苹果会⾃动重成员变量的顺序,将占⽤不⾜ 8 字节的成员挨在⼀起,凑满 8 字节,以达到优化内 存的⽬的。(感觉像开了上帝视角)

image.png 看到打印结果,0x00000012007b0005 这段内存地址存放了age=18,number=123,a=5;自动重排顺序,将不足8字节的成员挨在⼀起。

  1. 联合体(union)

联合体⼜叫共⽤体,union就是在内存中划了⼀个⾜够⽤的空间,⾄于你怎么玩它不管!联合体的成员变量就相当于为这块内存空间开辟了⼏个访问途径,他们共享这⼀块内存。

联合体的⼤⼩计算奉⾏俩个规则

1.联合体⼤⼩必须能容纳联合体中最⼤的成员变量

2.通过1计算出的联合体⼤⼩必须是联合体中占内存⼤⼩最⼤的基本数据类型⼤⼩的整数倍

image.png

  1. 联合体和结构体的区别

结构体(struct)中所有变量是“共存”的,⽽联合体(union)中是各变量是“互斥”的,只能存在⼀个。

struct内存空间的分配是粗放的,不管⽤不⽤,全部分配。这样带来的⼀个坏处就是对于内存的消

耗要⼤⼀些。但是结构体⾥⾯的数据是完整的。

联合体⾥⾯的数据只能存在⼀个,但优点是内存使⽤更为精细灵活,也节省了内存空间。

        NSLog(@"%lu %lu",sizeof(struct1),sizeof(struct2));

        //t1.name = "LG";

        //t1.age = 18;

        t1.height = 2.2;

        t2.name = "LG";

        t2.age = 18;

        t2.height = 2.2;


        NSLog(@"%p %p %p",&t2,&t2.age,&t2.height);

        NSLog(@"%lu",sizeof(t2));

        //结构以和联合体区别?

        

        //结构以 -- 共存 -- 内存开辟比较粗放

        //联合体 -- 互斥 -- 节省内存空间

        

        //1.联合体必须能够容纳最大的成员变量 //4 7 -- 8

        //通过1计算出来的大小必须是其最大成员变量(基本数据类型)的整数倍

        NSLog(@"%lu",sizeof(t3));
  1. 位域

位域的宽度不能超过前⾯数据类型的最⼤⻓度,⽐如int占4个字节也就是32位,那后⾯的数字就不能超过32。

⼀个位域存储在同⼀个字节中,如⼀个字节所剩空间不够存放另⼀位域时,则会从下⼀单元起存放该位域。 位域能够节省⼀定的内存空间。

struct LGStruct1 {

    char a;

    char b;

    char c;

    char d;

}struct1;


struct LGStruct2 {

    // a: 位域名  32:位域长度

    int a : 32;

    char b : 2;

    char c : 7;

    char d : 2;

}struct2;
  1. nonPointerIsa

nonPointerIsa是内存优化的⼀种⼿段。isa是⼀个Class类型的结构体指针,占8个字节,主要是⽤ 来存内存地址的。但是8个字节意味着它就有8*8=64位。存储地址根本不需要这么多的内存空间。 ⽽且每个对象都有个isa指针,这样就浪费了内存。所以苹果就把和对象⼀些息息相关的东⻄,存在了这块内存空间⾥⾯。这种isa指针就叫nonPointerIsa。

isa64情况.jpeg

arm64 (模拟器)

uintptr_t nonpointer : 1;

uintptr_t has_assoc : 1;

uintptr_t weakly_referenced : 1;

uintptr_t shiftcls_and_sig : 52;

uintptr_t has_sidetable_rc : 1;

uintptr_t extra_rc : 8   //arc 引用计数
------------------------------------------------
arm64 (真机)

uintptr_t nonpointer : 1;

uintptr_t has_assoc : 1;

uintptr_t has_cxx_dtor : 1;

uintptr_t shiftcls : 33;

uintptr_t magic : 6;

uintptr_t weakly_referenced : 1;

uintptr_t unused : 1;

uintptr_t has_sidetable_rc : 1;

uintptr_t extra_rc : 19
------------------------------------------------
x86_64

uintptr_t nonpointer : 1;

uintptr_t has_assoc : 1;

uintptr_t has_cxx_dtor : 1;

uintptr_t shiftcls : 44;

uintptr_t magic : 6;

uintptr_t weakly_referenced : 1;

uintptr_t unused : 1;

uintptr_t has_sidetable_rc : 1;

uintptr_t extra_rc : 8

image.png x86的代码图。就不一一列图了。 我们来验证一下查看下shiftcls(内存地址右向左,由低位到高位) image.png

image.png

总结

1.OC对象⾥⾯存储了⼀个isa指针 + 成员变量的值影响了对象内存。

2.联合体(union)和结构体(struct),表现在变量的联合体互斥 结构体共存

3.位域可以设置变量占用的位数,而节约系统内存。

4.重点探索了isa = nonPointerIsa 对应下x86模拟器的结构体,在通过<<>>运算符验证shiftcls类的内存地址。