iOS之内存对齐

关于iOS的内存对齐,首先我们思考一个问题,iOS的对象实例在内存中是如何分布的?带着这个问题我们往下看。

OC对象的内存分布

这里有一段代码,在main函数中,实例化一个person对象,通过x/4gx来打印对象实例的内存分布,如下图:


截屏2020-09-09 下午1.16.31.png

从图中的打印结果,我们看到:对象实例的内存从 0x6000001b1a40 开始,内存分布如下图:


image.png

我们看到了 person 对象在内存中的分布,但每个地址又分别代表什么呢?我们通过 po 打印一下每个地址。
(lldb) x/4gx person
0x6000001b1a40: 0x000000010bc506a0 0x0000000000000000
0x6000001b1a50: 0x000000010bc4e038 0x000000010bc4e058
(lldb) po 0x000000010bc506a0
PYPerson

(lldb) po 0x000000010bc4e038
Devin

(lldb) po 0x000000010bc4e058
码农

(lldb) 

根据打印的结果,绘成图也就是这样的:


image.png

从图中我们看到,为什么 0x6000001b1a48 指向的内容为空呢?这时我们把 age 和 h 属性的注释打开,重新运行看一下

(lldb) x/4gx person
0x6000033ade80: 0x00000001062596b0 0x0000001200000061
0x6000033ade90: 0x0000000106257038 0x0000000106257058
(lldb) po 0x12
18

(lldb) po 0x61
97

(lldb) po 0x00000001062596b0
PYPerson

(lldb) po 0x0000000106257038
Devin

(lldb) po 0x0000000106257058
码农

(lldb) 

通过打印结果,我们得出一个结论,第一个8字节的内存空间存储的是isa(地址),第二个8字节空间存储的是age和h,第三个是name,第四个是job,也就是:


image.png

看到person对象实际的内存分布后,我们再看一下PYPerson的属性声明:

@interface PYPerson : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *job;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) char h;

@end

这时,是不是又发现另外一个问题,为什么name和job在内存中排在了age和h的后边?

结构体的内存对齐

这个问题我们先放在这里,先来看一下后边这段代码:

struct PYStudent1 {
    double height;
    int age;
    char code;
    short score;
} student1;

struct PYStudent2 {
    double height;
    char code;
    int age;
    short score;
} student2;

这里声明了两个结构体,在main函数里打印这两个结构体的大小:

NSLog(@"student1 --> %lu, student2 --> %lu", sizeof(student1), sizeof(student2));

大家觉得这两个结构体的大小应该是多少呢?是不是相等的?下面为大家提供这几种类型所占的字节数(其他类型大家可自行查阅)。


image.png

根据表中提供的各类型数据所占字节数,student1和student2是不是都是15字节呢?我们运行程序看一下打印结果:

student1 --> 16, student2 --> 24

为什么student1和student2大小不相等?😲😲😲😲😲😲
这里就引出了我们今天的主题:内存对齐

内存对齐原则:
1.数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第⼀个数据成员放在offset为0的地⽅,以后每个数据成员存储的起始位置要从该成员⼤⼩或者成员的⼦成员⼤⼩(只要该成员有⼦成员,⽐如说是数组,结构体等)的整数倍开始。(⽐如int为4字节,则要从4的整数倍地址开始存储)
2.结构体作为成员:如果⼀个结构⾥有某些结构体成员,则结构体成员要从其内部最⼤元素⼤⼩的整数倍地址开始存储。(struct a⾥存有struct b,b ⾥有char、int、double等元素,那b应该从8的整数倍开始存储)
3.收尾⼯作:结构体的总⼤⼩,也就是sizeof的结果,必须是其内部最⼤成员(包括成员的成员)的整数倍,不⾜的要补⻬。

struct PYStudent1 {
    double height;  // 8字节  【0 -- 7】
    int age;        // 4字节  【8 -- 11】(8是4的倍数,所以从8开始)
    char code;      // 1字节  【11】(11是1的倍数)
    short score;    // 2字节  【12 -- 13】(12是2的倍数)
} student1;        
// 内存中占用 0-13字节,共14字节,根据内存对齐原则,总内存是8的倍数,所以是16字节

struct PYStudent2 {
    double height;  // 8字节  【0 -- 7】
    char code;      // 1字节  【8】
    int age;        // 4字节  【12 -- 15】
    short score;    // 2字节  【16 -- 17】
} student2;
// 内存中占用 0-17字节,共18字节,根据内存对齐原则,总内存是8的倍数,所以是24字节

从student1和student2的大小可以看出,虽然变量的个数和类型都相同,但是变量的顺序,会导致两个结构体的大小不同。
这也就是为什么 PYPerson 的name和job属性,声明的时候在age和h的前边,实际内存中却在age和h的后边。所以苹果其实对内存做了重排,以节省内存空间。

@interface PYPerson : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *job;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) char h;

@end

最后一个问题:为什么要做内存对齐?引用百度百科的说法:

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

扩展:

student3占用多少内存?

struct PYStudent3 {
    double height;
    char code;
    struct PYStudent1 s;
    int age;
    short score;
} student3;
  1. height为8字节,占用第0-7位;
  2. code为1字节,因为8是1的倍数,所以占用第8位;
  3. s为16字节,根据第二条原则,PYStudent1里边最大的成员为8位字节,所以 s 开始的位置必须是8的倍数,所以从第16位开始,为16-31位;
  4. age为4位,所以是32-35位;
  5. score为2位,是占用36、37位。

5个成员共占用38位,根据第3条原则,结构体大小必须是内部最大成员的倍数,所以是8的倍数,也就是40字节。

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