1.影响对象内存的因素
1.1一些简单的指令
- po 输出对应的值
- p 输出类型、引用值、内存地址
- p/x 打印16进制
- p/o 打印8进制
- p/f 打印浮点
- p/t 打印二进制
- x 输出对象的内存
- x/4gx (以16进制的形式去打印后面对象的4个8字节的内存地址)后面:4指4个,g指每一个为8个字节的大小, x16进制的形势
1.2 影响对象内存因素
1.2.1成员变量例如如下类:
输出内存是32如下:
1.2.2 增加成员变量如下:
输出内存是48如下:
1.2.2 成员变量赋值是否有影响如下:
1.2.3 实例方法和类方法是否影响:
总结:影响对象内存的只有成员变量,成员变量是否赋值不影响对象的内存,类方法和实例方法不影响对象内存。
2.对象的内存分布
对象⾥⾯存储了⼀个isa指针 + 成员变量的值,isa指针是固定的,占8个字节,成员变量的值的存储的顺序是怎样的?
例如如下实例:
可以看出int和short存储在同一个8字节下,来优化存储;
当LGPerson中将属性char a删除后输出如下:
总结:可以看到对象的内存还是48,对象中的成员变量的值的存储属性和它的位置初始化顺序无关,在对象的内部是以8字节进⾏对⻬的。苹果会⾃动重成员变量的顺序,将占⽤不⾜ 8 字节的成员挨在⼀起,凑满 8 字节,以达到优化内存的⽬的。
3.联合体、位域
3.1位域
位域为一种数据结构,可以把数据以位的形式紧凑的储存,并允许程序员对此结构的位进行操作。
- 位域的宽度不能超过前⾯数据类型的最⼤⻓度,⽐如int占4个字节也就是32位,那后⾯的数字就不能超过32。
- ⼀个位域存储在同⼀个字节中,如⼀个字节所剩空间不够存放另⼀位域时,则会从下⼀单元起存放该位域。
- 位域能够节省⼀定的内存空间。
例如下图中struct1中a,b,c,d公用一个字节,struct2中a4个字节,b、c、d共用一个字节,struct3中a分配一个字节,b和c分配一个字节,d分配一个字节,他们占的内存如下图:
位域虽然能够节省空间,但是节约的空间十分有限,所以在我们日常开发中基本不用,只有系统级别的才去这样用。
3.2联合体(union)
联合体⼜叫共⽤体,union就是在内存中划了⼀个⾜够⽤的空间,⾄于你怎么玩它不管!联合体的成员变量就相当于为这块内存空间开辟了⼏个访问途径,他们共享这⼀块内存。联合体的⼤⼩计算奉⾏俩个规则:
- 联合体⼤⼩必须能容纳联合体中最⼤的成员变量;
- 通过1计算出的联合体⼤⼩必须是联合体中占内存⼤⼩最⼤的基本数据类型⼤⼩的整数倍。
例如如下图中t2中的公用同一个内存,当一个成员变量赋值时候其它成员变量也改变:
联合体和结构体的区别
结构体(struct)中所有变量是“共存”的,⽽联合体(union)中是各变量是“互斥”的,只能存在⼀个。struct内存空间的分配是粗放的,不管⽤不⽤,全部分配。这样带来的⼀个坏处就是对于内存的消耗要⼤⼀些。但是结构体⾥⾯的数据是完整的。联合体⾥⾯的数据只能存在⼀个,但优点是内存使⽤更为精细灵活,也节省了内存空间。
4.nonPointerIsa
4.1 _class_createInstanceFromZone
我们在探索alloc方法的是最走到_class_createInstanceFromZone中的calloc方法中,开辟了内存空,当走过initInstanceIsa或者initIsa 的时候,才将内存和LGPerson关联起来,如下图:
4.2 initIsa
最终都进入initIsa中:
其中isTaggedPointer表示指针优化,从newisa.extra_rc = 1可以看出alloc出来的对象引用计数是1.
4.3 isa_t
进入isa_t如下图:
可以看出isa是一个联合体,是为了兼容旧版本;从而引入了nonpointerIsa,在ISA_BITFIELD结构体中。
4.4 nonPointerIsa
4.4.1 nonPointerIsa是什么:
nonPointerIsa是内存优化的⼀种⼿段。isa是⼀个Class类型的结构体指针,占8个字节,主要是⽤来存内存地址的。但是8个字节意味着它就有8*8=64位。存储地址根本不需要这么多的内存空间。⽽且每个对象都有个isa指针,这样就浪费了内存。所以苹果就把和对象⼀些息息相关的东⻄(例如引用计数),存在了这块内存空间⾥⾯。这种isa指针就叫nonPointerIsa。
4.4.2 nonPointerIsa的储存的内容:
- nonpointer:表示是否对isa指针开启指针优化,0:纯指针,1:不止是对象地址,isa中包含了类信息、对象的引用计数等;
- has_cxx_dtor:该对象是否有C++或者Objc的析构器,如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象;
- shiftcls:存储类指针的值,开启指针优化的情况下,在arm64(真机)架构中由33位用来存储类指针。
- magic:用于调试器判断当前对象是真的对象还是没有初始化的空间;
- weakly_referenced:标志对象是否被指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放;
- deallocating:标志对象是否正在释放内存;
- has_sidetable_rc:是否需要使用sidetable来存储引用计数;
- extra_rc: 表示该对象的引用计数值;
4.4.3 isa指针的数据结构和ISA_MASK
arm64(模拟器)
# define ISA_MASK 0x007ffffffffffff8ULL
# define ISA_MAGIC_MASK 0x0000000000000001ULL
# define ISA_MAGIC_VALUE 0x0000000000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 0
# define ISA_BITFIELD
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
arm64(真机)
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD
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
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD
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
5.如何运用isa的位运算得到类对象
5.1通过位运算计算
先右移动3位将右边清零,在左移31位,在右移动28位回到原来位置如下:
如下图可以验证:
5.2 通 & ISA_MASK得到:
6. new方法:
从源码中可以看到new方法调用了如下方法callAlloc 和 init方法,:
从汇编探索可以看到new调用了objc_opt_new方法,在源码中也调用到callAlloc 和 init方法中,
7.总结
- 影响对象内存的是成员变量:对象中存了isa指针和成员变量的值,isa占8个字节,所以影响对象内存的是成员变量(属性也会生成成员变量)。
- 在对象的内部是以8字节进⾏对⻬的:苹果会⾃动重成员变量的顺序,将占⽤不⾜8字节的成员挨在⼀起,凑满8字节,以达到优化内存的⽬的。
- 联合体和位域都可以优化内存空间,联合体中的成员都是互斥的,共用同一个内存。
- new方法就是alloc和init方法。