《跟我学》之OC内存对齐原理

1. 内存对齐原理

上一篇提到了对象在alloc创建时内存分配情况,
这一篇我们来更加深入的来研究一下什么叫内存对齐

1.1 准备工作

话不多说,为了方便直观的体现差异,定义一个宏,打印对象的 isa、内存地址、对象类型占用的内存大小对象实际占用的内存大小对象实际分配的内存大小。一定要注意占用跟实际分配的区别,是不一样的。等一下看日志就一目了然。
照旧万年祖传观察对象日志代码

#define Log(_var) ({ NSString *name = @#_var; NSLog(@"%@: %@ -> %p || 对象类型占用的内存大小%lu, 对象实际占用的内存大小%lu, 对象实际分配的内存大小%lu ", name, [_var class], &_var, sizeof(_var), class_getInstanceSize([_var class]) ,malloc_size((__bridge const void*)(_var))); })

1.2 对象

昨天我们研究了人Person类。今天不研究人,研究Dog狗类🐶并且附上源代码已经运行结果日志

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>

@interface Dog : NSObject
@end

@implementation Dog
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Dog *dog = [Dog alloc];
        Log(dog);
        /*
         * dog: Dog -> 0x7ffeefbff588 || 对象类型占用的内存大小8, 对象实际占用的内存大小8, 对象实际分配的内存大小16
         */
    }
    return 0;
}

我们这个无属性dog对象实际分配的内存大小16跟我昨天说的一样没有骗大家。这一点可以放心。因为是无属性对象实例,该对象只有isa指针所以只占用了8个大小的内存空间。

@property (nonatomic, strong) NSString *name;
如果我们给这个对象定义一个属性时打印如下

对象类型占用的内存大小8, 对象实际占用的内存大小16, 对象实际分配的内存大小16

@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) int age;
如果我们给这个对象定义两个属性时打印如下

对象类型占用的内存大小8, 对象实际占用的内存大小24, 对象实际分配的内存大小32

@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) int height;
如果我们给这个对象定义三个属性时打印如下

对象类型占用的内存大小8, 对象实际占用的内存大小24, 对象实际分配的内存大小32

我们发现随着属性的增加实际的分配内存大小符合昨天我们研究的对象创建16字节对齐原则。
对象类型占用的内存大小始终为8我猜测是对象指针的占用大小
对象实际占用的内存大小属性以8字节对齐排列
我们来给dog属性赋值之后

Dog *dog = [Dog alloc];
dog.name = @"旺财";
dog.age = 2;
dog.height = 80;

x/4gx dog一下看一下内存情况

0x104007b50: 0x001d800100002315 0x0000000200000001
0x104007b60: 0x0000000100001018 0x0000000000000000
图1

我们发现 0x0000000100001018是设置的name0x0000005000000002居然po不出来。
我们尝试po 0x00000050height,po 0x00000002age。原来heightage共用了内存段节约内存

1.3结论

以上这个例子来进行说明 苹果中属性重排,即内存优化因为age跟height各占用8比较浪费。虽然分配了32。但是一共占用24。跟你定义属性的顺序无关。最终会按着最优的方式重新排列

2 结构体

对象研究完了。我们再来看一下结构体,看一下通过结构体的方式是否能验证些什么?

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

struct Dog2{
    double b;   //8字节
    int c;      //4字节
    short d;    //2字节
    char a;     //1字节
}Dog2;
NSLog(@"狗一大小%lu--狗二大小%lu",sizeof(Dog1), sizeof(Dog2));
/*狗一大小24--狗二大小16*/

我们发现同是 4 个属性,同样包含char,double,int,short4个类型,只是顺序不同罢了。最后大小缺不一样
莫非就是我们之前说的内存对齐?只是对象不需要你手动排列。而结构体需要你手动排列才生效,那么它的规则是什么呢?

内存对齐规则

一般内存对齐的原则主要有以下三点
1、数据成员的对齐规则可以理解为min(m, n) 的公式, 其中 m表示当前成员的开始位置, n表示当前成员所需要的位数。如果满足条件 m 整除 n (即 m % n == 0), n 从 m 位置开始存储, 反之继续检查 m + 1 能否整除n, 直到可以整除, 从而就确定了当前成员的开始位置。
2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)
3、结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补⻬

我们通过简单的计算来验证一下

结构体Dog1 内存大小计算
根据内存对齐规则计算Dog1的内存大小,详解过程如下:

变量 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
因此 Dog1 的需要的内存大小为 15 字节,而 Dog1 中最大变量的字节数为 8 ,所以 Dog1 实际的内存大小必须是 8 的整数倍,18 向上取整到 24,主要是因为 248 的整数倍,所以 sizeof(Dog1) 的结果是 24

结构体 Dog2 内存大小计算
根据内存对齐规则计算 Dog2 的内存大小,详解过程如下:

变量 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)20 可以整除2,即 12-13 存储 d
变量 a :占 1 个字节,从 14 开始,此时 min(14, 1) ,即 14 存储 a
因此Dog2的需要的内存大小为 15字节,而 Dog2 中最大变量的字节数为 8,所以 Dog2 实际的内存大小必须是 8 的整数倍, 15 向上取整到 16 ,主要是因为 168 的整数倍,所以 sizeof(Dog2) 的结果是 16

不知道谁最大?我们来先看一个表,下表是对不同数据类型所占用的内存大小(纯手打,可复制)
有需要的点个关注。评论查眼👀Mark老司机开车从此不迷路

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