iOS中的内存对齐

内存对齐应该是编译器的管辖范围。编译器为程序中的每个数据单元安排在适当的位置上。

对齐原因:

  1. 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

规则定义:

  1. 数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
  2. 结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
  3. 结合1、2可推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。

规则解析:

  1. 规则1中表明数据成员的存放是按照定义的顺序依次存放的
  2. #pragma pack是对齐系数,每个平台不一样,程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数(32位平台一般为4,64位平台一般为8)。iOS下默认为8。这个数值大家可以通过调试#pragma pack(n)测试验证得到。
  3. 规则1,当第x(x>1)个成员y存放的时候,y按照min(n,m)来对齐存放,其中n为对齐系数,m为成员y的数据类型长度。
  4. 在完成各个数据成员的存放排列后,通过规则2,取min(n,maxM)进行对齐,其中n为对齐系数,maxM为所有数据成员类型中长度的最大值。

基础知识:

1.一个字节包含8个二进制位
2.一个十六进制位可用4个二进制位表示
3.一个字节可以由2个十六进制位表示

  1. 0x0000 0000 0000 0008表示16个16进制位,可以表示8个字节
    所以8可以用8个字节0x0000 0000 0000 0008表示,或者4个字节0x0000 0008,或者2个字节0x0008,取决于定义8的数据类型。
  2. 字符'a'换成ASCII码为97,可以用 0x61表示。
  3. iOS系统的编译平台是按照小端法进行编译。

实例分析:

struct Person {
    char a;
    long b;
    int c;
    short d;
}MyPerson;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        MyPerson.a = 'a';
        MyPerson.b = 8;
        MyPerson.c = 4;
        MyPerson.d = 2;
        NSLog(@"Adress=======MyPerson:%p",&MyPerson);  
        NSLog(@"Size=======MyPerson:%lu",sizeof(MyPerson));

    }
    return 0;
}

第一个成员char类型的成员a='a'占用1字节,此时:
a: 0x61

第二个成员long类型的成员b=8占用8个字节,根据规则解析3,b=8按照min(8,8)=8对齐,b的起始位置为8的倍数,不满足,a需要补齐7个字节保证b的起始位置为8的倍数
此时:
a:0x0000 0000 0000 0061
b:0x0000 0000 0000 0008

第三个成员int类型的成员c=4占用4个字节,根据规则解析3,整数c=4需要按照min(8,4)=4进行对齐,c的起始位置需要为4的整数倍,现在已经满足
此时:
a:0x0000 0000 0000 0061
b:0x0000 0000 0000 0008
c:0x0000 0004

第四个成员short类型的整数d=2占用2个字节,根据规则解析3,d按照min(8,2)=2进行对齐,d的起始位置需要为2的整数倍,现在已经满足
此时:
a:0x0000 0000 0000 0061
b:0x0000 0000 0000 0008
c:0x0000 0004
d:0x0002

根据规则解析4,结构体需要进行整体对齐,取min(n,maxDataLength) = max(8,8) = 8对齐,现在为8+8+4+2=22字节,需要补2个字节,按照排列顺序,在d占用内存段补2个字节;

最后得到
a:0x0000 0000 0000 0061
b:0x0000 0000 0000 0008
c:0x0000 0004
d:0000 0002
其中我们看把c和d看成共占用一段8字节的内存,因为对齐系数为8,编译器按照8的整数倍来读取内存地址。

按照小端法进行修正,此时内存排列应该内应该是
a:0x0000 0000 0000 0061
b:0x0000 0000 0000 0008
dc:0x0000 0002 0000 0004,
其中dc:0x0000 0002 0000 0004的第1-8位表示成员d的值,右边第9-16位表示成员c的值

综上,MyPerson结构体整体占用8+8+8=24字节


结构体嵌套结构体:

struct MyPerson1{
    char a;
    long b;
    int c;
    short d;
}MyPerson1;

struct MyPerson2{
    char a;
    long b;
    int c;
    short d;
    struct MyPerson1 person; 
}MyPerson2;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        MyPerson1.a = 'a';
        MyPerson1.b = 8;
        MyPerson1.c = 4;
        MyPerson1.d = 2;
        
        MyPerson2.a = 'a';
        MyPerson2.b = 8;
        MyPerson2.c = 4;
        MyPerson2.d = 2;
        MyPerson2.person = MyPerson1;
        
        NSLog(@"Adress=======MyPerson1:%p",&MyPerson1);
        NSLog(@"Adress=======MyPerson2:%p",&MyPerson2);
        NSLog(@"Size=======MyPerson2.person:%lu", sizeof(MyPerson2.person));
        NSLog(@"Size=======MyPerson2:%lu", sizeof(MyPerson2));

    }
    return 0;
}

输出如下:



person是一个结构体,最大成员为long类型,长度为8,所以按照min(8,8) 来对齐存放,已满足条件,所以直接存储即可,因此MyPerson2结构体整体占用48字节。

类的内存对齐:

定义一个类

@interface Teacher : NSObject

@property (nonatomic, assign) char a;
@property (nonatomic, assign) long b;
@property (nonatomic, assign) int c;
@property (nonatomic, assign) short d;

@end

并在main.m中添加如下代码:

Teacher *t = [[Teacher alloc] init];
t.a = 'a';
t.b = 8;
t.c = 4;
t.d = 2;

NSLog(@"Adress=======t:%p",t);

输出如下:



可以看到,该对象的第一个八字节存储着isa的值,而对象的第2个八字节和第3个八字节这2个内存段存储了我们定义的成员a、b、c、d(准确表述为_a,_b,_c,_d)的值,说明编译器做了相应的优化,不会直接按照我们在类中定义成员的顺序生成构造对应的结构体。

class_getInstanceSize和malloc_size

class_getInstanceSize:依赖于<objc/runtime.h>,返回创建一个实例对象所需内存大小,不考虑malloc函数的话,内存对齐一般是以 8 字节对齐。
malloc_size:依赖于<malloc/malloc.h>,返回系统实际分配的内存大小,在Mac、iOS中的malloc函数分配的内存大小总是 16 的倍数。

新建一个类XYPerson继承于NSObject:

@interface XYPerson : NSObject
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) long height;
@property (nonatomic, assign) char c1;
@property (nonatomic, assign) float m_float;
@end
​```
在main函数中写上如下代码:
​```
#import "XYPerson.h"
#import <objc/runtime.h>
#import <malloc/malloc.h>
​
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        XYPerson *p = [[XYPerson alloc] init];
        p.name = @"Ring";      //  NSString   8
        p.age = 18;            //  int        4
        p.height = 188;        //  long       8
        p.c1 =  'a';           //  char       1
        p.m_float =  12.6;     //  float      4
        NSLog(@"申请内存大小为:%lu——-系统开辟内存大小为:%lu", class_getInstanceSize([p class]), malloc_size((__bridge const void *)(p)));
    }
    return 0;
}​

输出如下:


iOS中,对象的属性是8字节对齐,而对象是16字节对齐。
因为内存是连续的,通过 16 字节对齐规避风险和容错,可以防止访问溢出。同时,也提高了寻址访问效率,也就是空间换时间。

·

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271