美文网首页
《跟我学》之OC内存对齐原理

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

作者: 这货不是文熙 | 来源:发表于2020-09-09 03:21 被阅读0次

    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

    相关文章

      网友评论

          本文标题:《跟我学》之OC内存对齐原理

          本文链接:https://www.haomeiwen.com/subject/sbusektx.html