OC底层探索05-内存对齐
2020-09-12 本文已影响0人
Henry________
上篇对对象实际内存占用和内存分配计算做了解释,但是留了一个小坑:对象属性的大小计算。
众所周知对象最终是以结构体的形式存在,所以通过对结构体的大小的探索来延伸到对象.
内存对齐
普通结构体,例1:
struct HRStruct {
int c; //4
double a; //8
char b; //1
char d; //1
}HRStruct;
NSLog(@"HRStruct结构体大小: %lu",sizeof(HRStruct));
- 直接计算: 4 + 8 + 1 + 1 = 14
以下是输出结果:

为什么会这样呢,带着问题我们继续。
普通结构体,例2
struct HRStruct {
double a; //8
int c; //4
char b; //1
char d; //1
}HRStruct;
- 直接计算: 8 + 4 + 1 + 1 = 14
仔细观察这个结构体中的变量是相同的,不同的只是顺序.
以下是输出结果:

得出一个结论:根据顺序不同也会造成所占内存大小不同,可是为什么会这样呢?我们继续。
结构体嵌套,例3
struct HRStruct {
double a; //8
int c; //4
char b; //1
char d; //1
}HRStruct;
struct HRStruct2 {
double a; //8
int c; //4
char b; //1
struct HRStruct str; // 16
}HRStruct2;
- 直接计算: 8 + 4 + 1 + 16 = 29
以下是输出结果:

内存的计算并不是简单的加法,而是做了一定的调整.
内存对齐原则
内存的计算是按照以下规则:
- 【原则一】 数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要
从该成员大小或者成员的子成员大小的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储 - 【原则二】收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补⻬。
- 【原则三】结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b
里有char,int ,double等元素,那b应该从8的整数倍开始存储.),结构体作为成员也需要按最大成员的整数倍进行补⻬。
例子验证
各种类型大小,方便计算时查看
![]()
例1:
struct HRStruct {
int c; //4
double a; //8
char b; //1
char d; //1
}HRStruct;
-
通过内存布局图,直观的查看内存占用情况
例2:
struct HRStruct {
double a; //8
int c; //4
char b; //1
char d; //1
}HRStruct;
-
通过内存布局图,直观的查看内存占用情况
例3:
struct HRStruct {
double a; //8
int c; //4
char b; //1
char d; //1
}HRStruct;
struct HRStruct2 {
double a; //8
int c; //4
char b; //1
struct HRStruct str; // 16
}HRStruct2;
-
通过内存布局图,直观的查看内存占用情况!
内存优化(属性重排)
通过例1、例2的对比,我们得出一个结论:根据顺序不同也会造成所占内存大小不同。所以通过调整的成员顺序可以优化内存大小、优化内存的布局
,从而提高内存的读取效率
.
- 如果是
结构体中数据成员
是根据内存从小到大的
顺序定义的,根据内存对齐规则来计算结构体内存大小,需要增加有较大的内存padding即内存占位符,才能满足内存对齐规则,比较浪费内存
- 如果是
结构体中数据成员
是根据内存从大到小的
顺序定义的,根据内存对齐规则来计算结构体内存大小,我们只需要补齐少量内存padding
即可满足堆存对齐规则,这种方式就是苹果中采用的
,利用空间换时间,将类中的属性进行重排,来达到优化内存的目的
我们能想到这个点,苹果的开发工程师一定也想到了这个点。
我们通过一个对象的内存情况来验证一下我们的猜测:
@interface HRTest : NSObject
@property(nonatomic, copy)NSString *name;
@property(nonatomic, copy)NSString *hobby;
@property(nonatomic, assign)double height;
@property(nonatomic, assign)char a;
@property(nonatomic, assign)char ab;
@end
HRTest * t = [HRTest alloc];
t.name = @"Henry";
t.hobby = @"woman";
t.height = 180.0;
t.a = 'a';
t.ab = 'A';
打印结果:

- name、hobby是变量的第一个、第两个属性,为什么却存在第三、第四个指针中呢?
第一个指针:isa
指针

第二个指针:短类型重排

直接打印是乱码,但是仔细看还是有一点线索的。
- 重点是为什么第二个指针里存的最后两个
char
类型的变量,这里就是apple对对象进行了属性重排,也就是内存对齐
,验证了之前我们的猜测。
结构体是不进行属性重排的,只有对象才会进行属性重排
第五个指针:double类型
apple对
double
,float
进行了优化并不是直接存储。
验证一下:

总结
apple通过内存对齐
将对象的内存进行了极致的压榨
,这一点提现在很多地方,真的值得我们学习。