iOS 内存对齐探究

影响OC对象内存大小的因素

数据类型内存大小:


占用大小.png

代码分析

@interface QHPerson : NSObject

{
    //isa 8
    NSString *level; //8
}
@property (nonatomic, copy) NSString *name; //8
@property (nonatomic, copy) NSString *nickName; //8
@property (nonatomic, assign) int age; //4
@property (nonatomic, assign) long height; //8
@end

QHPerson *p = [[QHPerson alloc] init];
p.name = @"qinhan";
p.age = 18;
size_t instansSize =  class_getInstanceSize([QHPerson class]);
NSLog(@"instansSize :%lu",instansSize);

通过class_getInstanceSize获取实例的大小,

打印结果.png

根据我上面标注的结果是44,为什么打印结果是48内,实际上在alloc的时候苹果做了8字节的对齐处理,参考上篇文章
控制变量,我们添加个方法
添加方法.png

发现内存大小不变
减少成员变量或属性
注释成员变量.png

打印结果:
打印.png

发现内存大小变小
总结:对象分配的内存大小成员变量属性有关

OC对象内存分析

内存分析.png

然后我们去掉char1这个变量
内存分析.png

  • 可以看到第三段内存的变化0x0000001200000063,第二次0x0000000000000012,两次int所占的字节不一样,由此可见底层做了字节对齐的优化处理。
  • x/8gx p 是lldb简写指令,下面附上一张别人总结的,以便理解
    LLDB文档.png

pragma pack(show)

  • pragma pack(show) 的大致作用就是改变编译器的对齐方式,如果不设置默认是8,我们也可以通过show的置来改变。
  • 最终的对齐方式还是要取变量所占的字节对比,取更小的,min(a,show). 比如int是4字节,默认是8 ,该成员就会以4字节对齐。
    截屏2021-06-09 上午10.02.27.png

内存对齐原则

数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储。这个整数应该是编译器设置的值和成员类型的值中的小值。

2.当结构体嵌套了结构体时,作为数据成员的结构体的自身长度取内部结构体的最大成员的内存大小,比如结构体a嵌套结构体bb中有charintdouble等,去最大的作为b的自身长度为,这里取double类型为8,这个8也要于编译器设置的值进行对比,如果show = 4,那么应该去4.
3.结构体的内存大小必须是结构体最大成员内存大小的整数倍,不足的需要补齐

规则验证

下面定义了两个结构体来验证一下内存的大小

struct QHStruct1{
    char a;     //1字节
    double b;   //8字节
    int c;      //4字节
    short d;    //2字节
}QHStruct1;
struct QHStruct2{
    double b;   //8字节
    int c;      //4字节
    short d;    //2字节
    char a;     //1字节
}QHStruct2;

查看打印结果

打印结果.png

纳尼,打印结果不一样,同样的成员顺序不一样打印结果居然不一样。但是如果我们了解内存对齐规则,就不会这么认为了
通过上面的对齐规则,通过图表的形式展现了,
QHStruct1和```QHStruct2``的内存分布,与打印结果一致
结构体内存分布.png

计算结构体QHStruct1 内存大小详细流程

变量a:占1个字节,从0开始,此时min(0,1),即 0存储 a
变量b:占8个字节,从1开始,此时min(1,8)1不能整除8,继续往后移动,知道min(8,8),从8开始,即8-15 存储b
变量c:占4个字节,从16开始,此时min(16,4)16可以整除4,即 16-19 存储 c
变量d:占2个字节,从20开始,此时min(20, 2)20可以整除2,即20-21 存储 d

计算结构体QHStruct2 内存大小详细流程

变量b:占8个字节,从0开始,此时min(0,8),即0-7 存储b
变量c:占4个字节,从8开始,此时min(8,4)8可以整除4,即 8-11 存储c
变量d:占2个字节,从12开始,此时min(12, 2)12可以整除2,即12-13存储 d
变量a:占1个字节,从14开始,此时min(14,1),即 14存储a

结构体嵌套
struct QHStruct2{
    double b;   //8字节
    int c;      //4字节
    short d;    //2字节
    char a;     //1字节
}QHStruct2;

struct QHstruct3{
    double b;   //8字节 0-7
    int c;      //4字节 8 9 10 11
    short d;    //2字节 12 13
    char a;     //1字节 14
    struct QHStruct2 str; //
    
}QHstruct3;

其实嵌套后和没有嵌套逻辑一样,只不过这里的str要按照成员里面最大的字节8来计算,相当于n =8, 这样就很容易算出,结构体从16出开始放,这样就是16+16=32字节

计算内存大小的几个方法

class_getInstanceSize
sizeof
malloc_size
举个例子

- (void)test1
{
    QHPerson *p = [[QHPerson alloc] init];
    
    p.name = @"qinhan";
    p.age = 18;
    p.nickName = @"123";
    //p.height = 180;
    p->level = @"青铜";
   // p.char1 = 'c';
    
    size_t instansSize =  class_getInstanceSize([QHPerson class]);
    NSLog(@"sizeOf :%lu",sizeof(p));
    NSLog(@"instansSize :%lu",instansSize);
    NSLog(@"objc对象实际分配的内存大小:%lu",malloc_size((__bridge const void*)(p)));
}

打印结果


截屏2021-06-08 下午6.36.07.png

发现instansSize 计算的大小和malloc_size计算大小不一样,
这是因为instansSize8字节对齐,也就是是8的倍数
imalloc_size16字节对齐,也就是16的倍数

内存优化

通过上面结构体内存对齐以及上面的内存分析,发现属性的顺序不一样,内存大小不一样,所以苹果底层做了一些优化.用空间换时间,将类中的属性进行重排,以此来达到内存优化的目的

你可能感兴趣的:(iOS 内存对齐探究)