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自己。这样就形成了一个完美的闭环。
如下图所示:
对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);
}
本文参考地址