内存字节对齐原则
1. 数据成员对⻬规则
结构体(struct)或联合体(union)的数据成员,第一个数据成员放在 offset 为 0 的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储
2. 结构体作为成员
如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b, b里有 char, int, double 等元素,那b应该从8的整数倍开始存储)。
3. 收尾工作
结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍.不足的要补⻬。
我们来看一个简单而又经典的例子
最终输出的结果是 24 -- 16
struct WYStruct1{
char a; // 1 + 7 补齐7字节成为8(随后跟着的 double 大小)的倍数,原则一
double b; // 8
int c; // 4
short d; // 2 + 2 补齐2字节让结构体的大小成为最大成员大小double(8字节)的倍数,原则二
}MYStruct1;
struct WYStruct2{
double b; // 8
int c; // 4
char a; // 1 + 1 原则一
short d; // 2
}MYStruct2;
其实对象当然也遵循这个原则
如下图打印是 40 --- 48
WYPerson *p2 = [WYPerson alloc];
// isa 8
p2.name = @"云先生"; // NSString 8
p2.age = 18; // int 4
p2.height = 180; // long 8
p2.title = @"测试"; // NSString 8
NSLog(@"%@",p2);
NSLog(@"%lu - %lu",class_getInstanceSize([p2 class]),malloc_size((__bridge const void *)(p2)));
那为什么同样一个对象,所占字节却不相同呢?
首先需要指出的是,class_getInstanceSize是对象需要的真正的内存是多少,class_getInstanceSize最终的36会遵循内存对齐的原则,参照8字节对齐(也就是说8字节对齐完全够用,16字节对齐是为了避免一些不必要的错误出现),参照的对象里面的属性,得到40字节,而malloc_size内部是16字节对齐,参照的是整个对象,得到的48。
从而我们得出结论:对象申请的内存大小 != 系统开辟的内存大小
但是到了这里又会出现一个小小的疑问,本身40字节就是采用内存对齐原则,多给了4字节的空间,为什么系统开辟内存的时候却给到了48字节,这不是浪费空间吗?
解答:上面分析了,40字节,是参照对象内的属性得到的,那么属性与属性之间是安全的,不会出现溢出和越界的问题,但是对象与对象之间呢?自然也需要进行容错处理来规避潜在的风险,系统采用16字节对齐的防守,多给一点空间。