Runtime-OC类/对象的内存

对象

一. NSObject对象分配内存大小

有两个方法可以得到一个指针对象的大小:

  • class_getInstanceSize(Class _Nullable cls): 得到编译器计算的大小, 即实际使用的大小
  • size_t malloc_size(const void *ptr): 得到实际分配的堆内存大小
#import <objc/runtime.h>
#include <malloc/malloc.h>

        NSObject *obj= [NSObject new];
        size_t size1 = class_getInstanceSize([NSObject class]);
        size_t size2 = malloc_size((__bridge void *)obj);
        NSLog(@"size1 = %zd, size2 = %zd", size1, size2);

输出:

size1 = 8, size2 = 16

可看到实际分配的堆内存大小>实际使用的内存大小
ref: https://forums.developer.apple.com/thread/114963

问题: oc为什么这样设计呢? 猜测:

  • 备用空间
  • 为什么是2倍? 正如方法缓存, 扩充时都是扩充一倍

二. 类对象组成

  1. 定义一个简单的类:
@interface Boy : NSObject
{
    @public
    int _weight;
}
@end

@implementation Boy
@end
  1. clang下:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

为什么指定生成cpp文件呢? 因为oc底层即cpp, 以免生成文件过大

生成文件中, 相关代码如下:

struct Boy_IMPL {
    
    struct NSObject_IMPL NSObject_IVARS;
    int _weight;
};
 struct NSObject_IMPL {
 Class isa;
 };

即类BoyBoy_IMPL 结构体, 而Boy_IMPL 结构体中包含下面两个成员变量:

Class isa; //每个对象都有的隐藏成员变量
int _weight; //其它定义的成员变量

2.1 体现在NSObject与Boy对象的内存大小如下:

size_t objSize = class_getInstanceSize(NSObject.class);
size_t boySize = class_getInstanceSize(boy.class);
NSLog(@"objSize: %zd, boySize: %zd", objSize, boySize);

输出:

objSize: 8, boySize: 16

可看到:

  • 一个空的NSObject对象, 就有8个字节, 即isa指针的大小
  • Boy对象的内存大小为16, 因为内存对齐, 所以Boy对象大小为8的倍数

2.2 体现在内存实际内容:

  • 查看内存地址:


    Boy对象地址
  • 地址对应内存:


    内存地址
  • arm是大端架构, 低位在高地址. 只查看自己的内存: 前16字节即可.

  • 前8个字节是isa指针

  • 后4个字节是0x42, 即66, 我们给_weight赋的值

  • 最后4个字节是内存对齐空余出来的4个字节, 并已经初始化为0

2.3 LLDB读取内存也可以看到这16字节的值

内存值

类对象

Class 就是类对象, 类对象在内存中有且只有一份, class对象在内存中的信息包括:

  • isa指针
  • superclass指针
  • 类的成员变量, 属性(类型, 名称), 对象方法, 协议
  • 其它
    注: 类方法不保存在class对象中, 而是在元类中.

元类

得到元类API:

Class meta = object_getClass([Boy class]);

meta即为Boy的元类对象(meta-class)
每个类在内存中, 有且仅有一个元类对象, 元类内存结构同类对象, 其包括:

  • isa指针
  • superclass指针
  • 类的类方法信息
  • 其它

isa指针

  • instance的isa指向class
    调用对象方法时, 先通过isa找到class, 再找到对象方法的实现(IMP)进行调用
  • class的isa指向meta-class
    • 调用类方法时, 先通过isa找到meta-class, 再找到类方法的实现(IMP)进行调用
    • 子类调用父类方法时, 先通过isa找到meta-class, 再通过meta-class的superclass指针找到父类的meta-class, 最后找到类方法的实现(IMP)进行调用
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容