由 Alloc实现流程(一) 的试探开始,终于看到了一些底层实质性的源码,接演前文继续探究 instanceSize 的内部实现。
本文中心:
Alloc该为实例对象开辟了多大内存?- 根据类对象 的大小,以16字节对齐原则计算出要开辟的大小。
- 类对象的大小是多少?因素是什么?如何怎么计算的?
- 影响类大小的因素是其内部的成员/属性的数量,与方法无关。
- 依照结构体内存对齐原则,其实OC层终归是以8字节对齐计算类的大小。
- 结构体的内存对齐原则。
- 这个一句话概况不了,写下面了。 👇👇🏻👇🏼👇🏾👇🏿
- 简析8和16字节内存对齐的原因。
- 浅显一说:空间换时间,优化读取速度,减少CUP消化,容错。
理论伺候
1.1、跟一下instanceSize()流程
执行calloc() 之前,先跟一下instanceSize,看看内部方法是如何进行开辟内存大小的内存对齐的。
再调用
alloc之前, 会先执行realizeClassWithoutSwift对类对象的相关内存初始话,其中就包括了setFastInstanceSize,这就是取cache中类的大小。(这块会另起一篇,展开来说)
fast 中关键代买就是align16(),根据_flag取到的类对象 大小,以16字节对齐原则计算出要开辟的大小。
补充一下图中同理算法(align16->objc-os.h):
本质就是 x 在加上15之后,>>3后舍去低位,<<3补位后看看高位能否达到 x 下一个16字节对齐的点,如果能到也就升上去了,达不到就还是原来的大小了。
举个🌰:
假如 x = 24, 此时 x 16字节对齐的阶梯为32:
首先:x = 24, 二进制为:01 1000,
然后:x + 15, 二进制为:10 0111,
左右位移后:二进制为:10 0000, 十进制:32
再假如:x = 40, 二进制:10 1000,算算是不是升了?
1.2、结构体内存对齐
这就是3条纯粹的理论,类大小的计算就是在结构体内存对齐原则的基础上,以8字节对齐原则计算的。
1. 单纯数据成员对齐
结构体(Struct)或联合(Union)的第一个数据成员 起始为0,之后每个数据成员
i存放位置要从i本身字节大小整数倍的地址开始存。2. 结构体嵌套对齐
结构体
(A)的数据成员中包含其他结构体(B),那成员结构体(B)的存放位置要从其(B)内部最大元素的整数倍的位置开始存。3.结构体总大小
结构体
(A)的总大小,必须是其(A)内部最大成员 (一般都是8) 的整数倍,不足要补齐。
比如原则1+3:
Struct 和 StructOne 虽然内部成员的数据类型完全一样,但是依则计算万以后的大小缺不相同。
比如原则2+3:
StructTwo 的成员中包含了 Struct,其中 Struct 中最大元素a占8个字节,那 Struct e 存放的位置就应该是从24开始。
实践伺候
1. 结构体内存对齐
验证一下我举的大🌰,直接把刚才的代码跑起来,看看输出结果是否和理论时猜测一致。
实践的输出验证里理论的正确性。
2.类的大小
类的大小依照结构体内存对齐原则计算,不过在OC层一个空白的类也会有一个isa指针,至少都是8个字节。
在UIPerson里加了个char,此时 的大小应该是8+1,经过对齐算法后应该会变为16,看一下运行结果。
嗯~有图有真相,因为要区分操作系统,所以算法中用的是宏,64位中
#define WORD_MASK 7UL
3.影响类大小的因素
影响类大小的因素是其内部的成员/属性的数量,与方法无关。为了验证这个说法,设置三种情况来看看类大小的变化。
- 只有1个属性。
- 情况1 + 1个成员变量。
- 情况2 + 1个实例方法。
这...结果再这呢,不用细说了吧?
4. 实例对象Alloc了多少内存
找上面的UIPerson帮个忙,验证一下 实例对象alloc出内存大小是否根据类对象 的大小,以16字节对齐原则计算出的。
这波试探现在算是整闭环了,由
instanceSize起由instanceSize终,中心—>理论—>实践,完美~
总结
对象的大小、内存对齐说清楚了,不过在试探过程中也引出了关于类的相关内容、还有isa相关内容,等我另起一篇再更新(一定会的)。
资源汇总
- 所有图片截取代码都来自
objc4(818)中的objc-runtime-new和objc-os,行号如图。 - 对OC底层的疯狂试探-Alloc实现流程(一)