isa指针以及SuperClass

239 阅读2分钟

isa指针的指向

  1. instance实例对象的isa指针指向class类对象
  • 当调用对象方法时,通过instance对象的isa找到class,最后找到对象方法的实现进行调用。
  1. class类对象的isa指针指向meta-class元类对象
  • 当调用类方法时,通过class类对象的isa找到meta-class,最后找到类方法的实现进行调用。
  1. ==所有meta-class对象的isa指针指向基类的元类对象==。

SuperClass指针

继承关系:Student -> Person -> NSObject
  1. 类对象的SuperClass指针
  • 当Student的实例对象要调用Person的对象方法时,会先通过Student实例对象的isa指针找到Student的类对象,然后通过Student类对象的SuperClass指针找到Person的类对象,最后找到方法的实现进行调用。
  1. 元类对象的SuperClass指针
  • 当Student的类对象调用Person的类方法时,会先通过Student类对象的isa指针找到Student的元类对象,然后通过Student元类对象的SuperClass指针找到Person的元类对象,最后找到方法的实现进行调用。
  • ==基类的元类对象的superclass指针指向的是基类的类对象== isa、superclass关系图

isa指针细节

从64bit开始,isa要进行一次位运算才能计算出真实地址。

1

从objc源码中可以查到ISA_MASK的相关信息

类对象和元类对象的结构

struct objc_class的结构

//源码
#define FAST_DATA_MASK          0x00007ffffffffff8UL

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    
    class_rw_t *data() const {
        return bits.data();
    }
    
    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);//bits需要&FAST_DATA_MASK才能获取到class_rw_t数据
    }
    
    ...函数
}

struct class_rw_t {
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;
};

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
    uint32_t reserved;

    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;
};

    细看两个结构体的成员变量会发现很多相同的地方,他们都存放着当前类的属性、实例变量、方法、协议等等。区别在于:class_ro_t存放的是编译期间就确定的;而class_rw_t是在runtime时才确定,它会先将class_ro_t的内容拷贝过去,然后再将当前类的分类的这些属性、方法等拷贝到其中。所以可以说class_rw_t是class_ro_t的超集,当然实际访问类的方法、属性等也都是访问的class_rw_t中的内容

  1. 类在内存中的位置是在编译期间决定的,在之后修改代码,也不会改变内存中的位置。
  2. 类的方法、属性以及协议在编译期间存放到了“错误”的位置,直到 realizeClass 执行之后,才放到了 class_rw_t 指向的只读区域 class_ro_t,这样我们即可以在运行时为 class_rw_t 添加方法,也不会影响类的只读结构。
  3. 在 class_ro_t 中的属性在运行期间就不能改变了,再添加方法时,会修改 class_rw_t 中的 methods 列表,而不是 class_ro_t 中的 baseMethods。