objc对象的内存布局和Class的本质

一个objc对象如何进行内存布局?

所有父类的成员变量和自己的成员变量都会存放在该对象所对应的存储空间中.

Objective-C 对象的结构图
ISA指针
根类的实例变量
倒数第二层父类的实例变量
...
父类的实例变量
类的实例变量
  • 根对象就是NSObject,它的superclass指针指向nil

  • 类对象既然称为对象,那它也是一个实例。类对象中也有一个isa指针指向它的元类(meta class),即类对象是元类的实例。元类内部存放的是类方法列表,根元类的isa指针指向自己,superclass指针指向NSObject类。


什么是Class?

每一个对象内部都有一个isa指针,指向他的类对象,类对象中存放着本对象的

  1. 对象方法列表(对象能够接收的消息列表,保存在它所对应的类对象中)
  2. 成员变量的列表,
  3. 属性列表,

Class类的源码:


struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;//isa指针指向Meta Class,因为Objc的类的本身也是一个Object,为了处理这个关系,runtime就创造了Meta Class,当给类发送[NSObject alloc]这样消息时,实际上是把这个消息发给了Class Object
};

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;// 父类
    cache_t cache;  // formerly cache pointer and vtable
    //方法缓存,对象接到一个消息会根据isa指针查找消息对象,这时会在method Lists中遍历,如果cache了,常用的方法调用时就能够提高调用的效率。
    
    // bits & FAST_DATA_MASK = class_rw_t 获取具体类信息
    class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
    
    class_rw_t *data() { 
        return bits.data();
    }
    
    .
    .
    .

}

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;// 类的版本信息

    const class_ro_t *ro;//class只读列表

    method_array_t methods;//方法列表
    property_array_t properties;//属性列表
    protocol_array_t protocols;//协议列表
    .
    .
    .
}

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;//instance对象暂用的内存大小
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;//类名
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;//成员变量列表

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};
  • class_ro_t结构体一般保存的是原本类编译完存在的最原始的类信息,所以为只读的。

  • class_rw_t结构体也是保存的类信息,但如果在程序运行过程中对类的methods(方法列表)、properties(属性列表)、protocols(协议列表)等进行更改时,则是在class_rw_t结构体上进行的。如给类添加分类


获取类对象和元类的方法:

  1. Class objc_getClass(const char *aClassName)

传入字符串类名,返回对应的类对象

#import 
const char * name = "Student";
Class c = objc_getClass(name);

2.Class object_getClass(id obj)

传入的obj可能是instance对象、class对象、meta-class对象

返回值 :

  • 如果是instance对象,返回class对象

  • 如果是class对象,返回meta-class对象

  • 如果是meta-class对象,返回NSObject(基类)的meta-class对象

#import 
Class objectClass = [NSObject class];
// 将类对象当做参数传入,获得元类对象
Class objectMetaClass = object_getClass(objectClass);

  1. - (Class)class、+ (Class)class

返回的是类对象

 - (Class) {
     return self->isa;
 }
 + (Class) {
     return self;
 }


Meta Class本身也是一个Class,它跟其他Class一样也有自己的 isa 和 super_class 指针。看下图:

image
  • 每个Class都有一个isa指针指向一个唯一的Meta Class

Class(struct objc_class)的isa指针向的地址值与 ISA_MASK 进行或运算后才为实际元类的地址值

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
  • 每一个Meta Class的isa指针都指向最上层的Meta Class(图中的NSObject的Meta Class)

  • 最上层的Meta Class的isa指针指向自己,形成一个回路

  • 每一个Meta Class的super class指针指向它原本Class的 Super Class的Meta Class。但是最上层的Meta Class的 Super Class指向NSObject Class本身

  • instance调用对象方法的轨迹
    isa找到class,方法不存在,就通过superclass找父类

  • class调用类方法的轨迹
    isa找meta-class,方法不存在,就通过superclass找父类


参考:

  1. 刨根问底Objective-C Runtime

你可能感兴趣的:(objc对象的内存布局和Class的本质)