网络火爆小demo

屏幕快照 2020-03-19 17.35.42.png
就这个家伙,网络上很火的小demo,一点点剖析它之前,先看一下原则。

内存对齐原则

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

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

3、收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补⻬。

原则解析

回到demo来看
StructOneStructTwo 都是单纯的普通结构体,先来看StructOne:

  1. struct StructOne{
  2. char a; //1字节
  3. double b; //8字节
  4. int c; //4字节
  5. short d; //2字节
  6. } one;

a 占1个字节,**直接放在offset为0的位置;**(原则一)
b 占8个字节,**他的**起始位置要从该成员大小(8)的整数倍开始,也就是从offset为8的位置开始存储;
c 占4个字节,**他的**起始位置要从该成员大小(4)的整数倍开始,也就是从offset为16的位置开始存储;d 占2个字节,他的起始位置要从该成员大小(2)的整数倍开始,也就是从offset为20的位置开始存储;
收尾:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍.不足的要补⻬。(原则三)
StructOne最大成员8字节,整数倍补齐,长度就是24。(如下图, 表示存储 表示空余

a b
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
c d
16 17 18 19 20 21 22 23

再来看StructTwo:a和b,c和d的存储顺序改变了;

  1. struct StructTwo{
  2. double b; //8字节
  3. char a; //1字节
  4. short d; //2字节
  5. int c; //4字节
  6. } two;

b 占8个字节,**直接放在offset为0的位置;**(原则一)
a 占1个字节,**他的起始位置要从该成员大小(1)的整数倍开始,也就是从offset为8的位置开始存储;
d 占2个字节,
他的**起始位置要从该成员大小(2)的整数倍开始,也就是从offset为10的位置开始存储;
c 占4个字节,**他的**起始位置要从该成员大小(4)的整数倍开始,也就是从offset为12的位置开始存储;
收尾:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍.不足的要补⻬。StructTwo最大成员8字节,长度16,无须额外补齐。(如下图)**

b a
d c
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

再来看一个结构体包含结构体的小栗子,先上结果:(这个下边的解释是我自己想的,如果有错误,以后更正)
屏幕快照 2020-03-19 22.37.28.png
StructFour中包含StructThree,**其中**StructThree中最大的字节为4字节,好,直接看StructFour的size:
a 占1个字节,直接放在offset为0的位置;(原则一)
然后存储
StructThree,这是个结构体,根据原则二:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储”StructThree最大元素4字节,所以从4的整数倍开始,StructThree中
b 占1个字节,但他是StructThree结构体的起始位置,要从StructThree内部最大元素大小的整数倍也就是(4)的整数倍开始,也就是从offset为4的位置开始存储;
**

c 占4个字节,他的起始位置要从该成员大小(4)的整数倍开始,也就是从offset为8的位置开始存储;
d 占2个字节,他的起始位置要从该成员大小(2)的整数倍开始,也就是从offset为12的位置开始存储;
收尾:结构体的总大小**,也就是sizeof的结果,必须是其内部最大成员的整数倍.不足的要补⻬。StructFour最大成员4字节,整数倍补齐为16。(如下图)
**

a b c d
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

class_getInstanceSize()和malloc_size()差别

先来看一下小栗子:
屏幕快照 2020-03-20 10.55.54.png屏幕快照 2020-03-20 10.57.30.png
class_createInstance都干了啥这篇文章的时候,跟的源码和class_getInstanceSize() 差不太多,直接跟进去看:得出这个流程(直接按照源码来看,源码的布置在alloc源码流程分析有提到)
class_getInstanceSize()—->alignedInstanceSize()—->word_align(unalignedInstanceSize());
unalignedInstanceSize()的值就是data()->ro->instanceSize;来获取当前类中属性的所占空间。
class_getInstanceSize()输出的40是怎么来的?明明按照字节对齐这几个属性,8字节对齐,应该是32字节啊。这里有一个主角光环,Person类继承自NSObject,这个NSObject带有一个isa指针,Person直接给继承过来了,所以40个字节。有图有凭证:
屏幕快照 2020-03-20 11.00.41.png
好,给你加上一个isa指针,那么malloc_size()为什么会是48呢?继续跟malloc_size()源码发现,点不进去了,这个需要配置一下malloc的源码,这里就不配置了,直接提一下最根本:malloc_size()获取的是CPU实际分配给对象的空间大小,对象之间的字节对齐是16字节对齐,所以申请的40进行16对齐的话,就是48。(算法和8字节对齐((x+7)& ~7)的算法一样,只不过16字节对齐就是((x+15)& ~15))。
对象申请40,CPU如果仅仅的给40个空间,容易搞出问题啊。你可能会想,在属性8字节对齐的时候,也会有多余空出的空间啊?但是!参考上边的结构体内存存储的表格,不难发现,空余的空间不一定在末尾啊,所以,CPU多分配一点是有必要的,属性之间安全了,对象之间也要保证安全啊。
简单记一下就是:
8字节对齐的参照物是对象里边的属性,
16字节对齐的参考物是整个的对象。
class_getInstanceSize(**):返回某个对象至少需要多少空间(8字节对齐)
malloc_size():返回的是CPU实际分配的空间(16字节对齐)**


先说这么多,有新的理解再补充或者纠正!