【MJ-iOS底层原理总结】自定义类内存占用大小

551 阅读2分钟

在上一篇文章 juejin.cn/post/697477… 中我们验证了一个NSObject对象占用16个字节的内存。那么我们这次来验证,自定义类占用多少内存?

  1. 首先我们创建一个Student类,并创建一个Student对象

    @interface Stuednt : NSObject{
        @public
        int _no;
        int _age;
    }
    @end
    
    @implementation Stuednt
    @end
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Stuednt *stu = [[Stuednt alloc] init];
        }
        return 0;
    }
    
  2. 将OC代码转换成C++源码进行分析:

    执行命令:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp

    得到C++源码如下:

    struct Stuednt_IMPL {
            struct NSObject_IMPL NSObject_IVARS;
            int _no;
            int _age;
    };
    
    /* @end */
    
    // @implementation Stuednt
    // @end
    
    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
            Stuednt *stu = ((Stuednt *(*)(id, SEL))(void *)objc_msgSend)((id)((Stuednt *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Stuednt"), sel_registerName("alloc")), sel_registerName("init"));
        }
        return 0;
    }
    
  3. 查看NSObject_IMPL结构体,其结构如下:

    struct NSObject_IMPL {
            Class isa;
    };
    

    所以我们就可以把Stuednt_IMPL的结构理解为:

    struct Stuednt_IMPL {
             Class isa;
             int _no;
             int _age;
     };
    

    整个转换过程,如下图所示: image.png

  4. Student内存布局如下: image.png

  5. 我们知道指针占内存大小为8个字节int占内存大小为4个字节,所以我们可以推断出Stuednt对象的成员变量占用的内存大小为:8+4+4 = 16个字节

  6. 在编译器中进行验证:

    Stuednt *stu = [[Stuednt alloc] init];     
    NSLog(@"%zd", class_getInstanceSize([Stuednt class]));
    NSLog(@"%zd", malloc_size((__bridge const void *)(stu)));
    

    得到结果:16 16

    所以我们得出了结论:Student对象的内存占用大小为16字节

  7. 接下来我们再验证一下,Person对象的内存占用大小为多少?

    @interface Person : NSObject{
        @public
        int _no;
        int _age;
        int _name;
    }
    @end
    

    直接在编译器中进行验证:

     Person *per = [[Person alloc] init];     
     NSLog(@"%zd", class_getInstanceSize([Person class]));
     NSLog(@"%zd", malloc_size((__bridge const void *)(per)));
    

    得到结果:24 32

    按照我们的推断来计算,8+4+4+4 = 24个字节,那么为什么会占用32个字节呢?

  8. 原因:这是因为系统内存对齐的原因导致的,执行alloc分配内存空间时,操作系统为了方便、快速的管理内存,在分配堆空间时,是一块一块的分配的,而不是一个字节一个字节的分配,这样操作系统对其的操作速度会最快。而在iOS系统中,每块内存都是16的倍数。所以当需要24个字节大小的内存空间时,操作系统就会分配32个字节

    • 相关线索:在苹果的libmalloc源码中,可以找到对应宏的注释:每块内存为16的倍数,最大为256:

    image.png

  9. 最后总结一下这两个获取内存占用大小的方法:

    • class_getInstanceSize:创建一个实例对象,至少需要多少内存?
    • malloc_size:创建一个实例对象,实际上分配了多少内存?
  10. 补充:如果想查看其他操作系统的内存对齐方式,可以在GNU官方网站中下载对应库的源码:www.gnu.org/software/li… image.png