iOS Runtime:Class 和 meta-class

1、Class

Objective-C类是由Class类型来表示的,它实际是一个指向objc_class结构体的指针。它的定义如下


typedef struct objc_class *Class;

objc_class结构体定义如下:

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
# endif

#if !__LP64__
#define FAST_DATA_MASK          0xfffffffcUL
#elif 1
#define FAST_DATA_MASK          0x00007ffffffffff8UL
#else
#define FAST_DATA_MASK          0x00007ffffffffff8UL
#endif

struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
    // ...
}

inline Class objc_object::getIsa() 
{
    if (!isTaggedPointer()) return ISA();

    uintptr_t ptr = (uintptr_t)this;
    if (isExtTaggedPointer()) {
        uintptr_t slot = 
            (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        return objc_tag_ext_classes[slot];
    } else {
        uintptr_t slot = 
            (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
        return objc_tag_classes[slot];
    }
}
// 获取到ISA指针
inline Class objc_object::ISA() 
{
    assert(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    // isa指针需要经过一次 & ISA_MASK操作之后才得到真正的地址
    return (Class)(isa.bits & ISA_MASK);
#endif
}

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() { 
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
  // 父类objc_object 中的获取去ISA指针的getIsa() 可以简写成下面
    objc_class *metaClass() {
        return (objc_class *)((long long)isa & ISA_MASK);
    }
  // ...
}
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;

    method_array_t methods;       // 函数列表
    property_array_t properties;   // 属性列表
    protocol_array_t protocols;    // 协议列表

    Class firstSubclass;
    Class nextSiblingClass;
    
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
  // ...
}

在这个定义中,下面几个字段是我们感兴趣的

(1) isa :需要注意的是在Objective-c中,所有类自身也是一个对象,这个对象Class里面也有一个isa指针,

它指向mateClass(元类) 这个后面会介绍

(2) super_class : 指向自己的父类,如果该类已经是最顶层的根类(如NSObject或NSProxy),则super_class为NULL。

(3) cache : 用来缓存最近使用的方法,一个对象接收到一个消息时,它会根据isa指针去查找响应这个消息的对象。在实际应用中这个对象的一部分方法是常用的,很多方法是很少用或者基本不用,在这种情况下如果每次收到消息都要把methodList遍历一遍,势必会影响性能。这时,cache就派上用场了。在我们每次调用过一个方法后,这个方法就会被缓存到cache列表中,下次调用的时候runtime就会优先去cache中查找,如果cache没有,才去methodLists中查找方法。这样,对于那些经常用到的方法的调用,但提高了调用的效率。

针对cache 我们用下面的例子说明一下


NSArray *array = [[NSArray alloc] init];

其流程是:

(1) [NSArray alloc]先被执行。先去NSArray中查找+alloc方法,因为NSArray没有+alloc方法,于是去父类NSObject去查找。

(2) 检测NSObject是否响应+alloc方法,发现响应,于是检测NSArray类,并根据其所需的内存空间大小开始分配内存空间,然后把isa指针指向NSArray类。同时,+alloc也被加进cache列表里面。

(3) 接着,执行-init方法,如果NSArray响应该方法,则直接将其加入cache;如果不响应,则去父类查找。

(4) 在后期的操作中,如果再以[[NSArray alloc] init]这种方式来创建数组,则会直接从cache中取出相应的方法,直接调用。

2、元类(metaClass)

在上面我们提到,所有的类自身也是一个对象,我们可以向这个对象发送消息(即调用类方法)。如:


NSArray *array = [NSArray array];

这个例子中,+array消息发送给了NSArray类,而这个NSArray也是一个对象。既然是对象,那么它也是一个objc_object指针,它包含一个指向其类的一个isa指针,这个isa指针也要指向这个类所属的类。那么这些就有一个问题了,这个isa指针指向什么呢?这就引出了meta-class的概念:

meta-class是一个类对象的类

当我们向一个对象发送消息时,runtime会在这个对象所属的这个类的方法列表中查找方法;而向一个类发送消息时,会在这个类的meta-class的方法列表中查找。

meta-class 是必须的,因为它为一个 Class 存储类方法。每个Class都必须有一个唯一的 meta-class,因为每个Class的类方法基本不可能完全相同。

再深入一下,meta-class也是一个类,也可以向它发送一个消息,那么它的isa又是指向什么呢?为了不让这种结构无限延伸下去,Objective-C的设计者让所有的meta-class的isa指向基类的meta-class,以此作为它们的所属类。即,任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类在一定程度上可以理解为若一个Class继承自NSObject,则这个Class的meta-class继承自NSObject的meta-class,而基类的meta-class的isa指针是指向它自己,这就是说 NSObject 的 meta-class 的 isa 指针指向NSObject 的 meta-class自己。这样就形成了一个完美的闭环。

如下图所示:

经典ipa指向图.png

对isa、superclass总结

instance的isa指向class
class的isa指向meta-class
meta-class的isa指向基类的meta-class,基类的isa指向自己
class的superclass指向父类的class,如果没有父类,superclass指针为nil
meta-class的superclass指向父类的meta-class,基类的meta-class的superclass指向基类的class
instance调用对象方法的轨迹,isa找到class,方法不存在,就通过superclass找父类
class调用类方法的轨迹,isa找meta-class,方法不存在,就通过superclass找父类

在用Runtime实现交换类方法黑魔法时 需要注意了 代码如下:


Class class =self.class;

Method originalMethod =class_getClassMethod(class, originalSelector);

Method replacementMethod =class_getClassMethod(class, replacementSelector);

Class mateClass = objc_getMetaClass(NSStringFromClass(class).UTF8String);

if (class_addMethod(mateClass, originalSelector, method_getImplementation(replacementMethod), method_getTypeEncoding(replacementMethod))) {
        class_replaceMethod(mateClass, replacementSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}
else{
        method_exchangeImplementations(originalMethod, replacementMethod);
 }

本文参考地址

你可能感兴趣的:(iOS Runtime:Class 和 meta-class)