目前iOS中,objc_class与objc_object使用的是后两个定义。
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
}
struct objc_object {
private:
isa_t isa;
}
这些结构体代表了类和对象在runtime中的数据结构
typedef struct objc_class *Class;
typedef struct objc_object *id;
使用重定义,Class 指向objc_class结构体的指针,id 指向objc_object结构体的指针
id obj;
Class cls;
定义的均是的指针变量
struct objc_class : objc_object,这说明每一个类实际上也是一个对象,有一个is a的指针
每一个对象都有一个is a的指针,通过该指针,我们能找到对象所属类。每一个类也有一个is a的指针,也是一个对象,那么,它也必须是另一个类的实例,这个类就是元类。元类也是一个对象,那么元类的is a指针又指向了哪里?
为了设计上的完整,所有的元类的is a指针都会指向一个根元类,根元类本身的is a指针指向自己,这样就形成了一个闭环。图片参考 iOS开发进阶 p217
通过上图,我们可以得出以下结论
1 类的类是元类
2 类的根类是NSObjet,NSObject的父类是nil
3 元类同样存在继承关系,元类的父类等价于父类的元类(类方法查找过程中的必须)
4 元类的根类是NSObjet的元类,NSObjet的元类isa指向自身。
5 NSObjet的元类的父类是NSObjet
每个类的类对象和元类对象 都仅有一个
消息发送机制,是对对象发送消息。也就是方法
@interface NSObject {
Class isa OBJC_ISA_AVAILABILITY;
}
由于继承关系,每个类都会有一个is a指针。
Person *ps = [Person new];
之前我的理解是创建一个结构体的指针(Class cls,Class经过重定义,创建出的变量,是指向objc_class的指针)。后来,又想到了C++中类和结构体的区别,然后整个人就懵逼了。
这里,是一个疑问,OC中的类本质是不是一个结构体。这里,一些解释是类比着结构体,比如我们每创建一个类实例,就像定义了一个对应结构的结构体变量,可能会有些不当,但有助于理解。在Runtime之外,还是说类和对象比较合适点。
类中定义了实例方法,元类中定义了类方法。
实例方法的调用规则是,根据is a指针找到类,如果该类没有定义一个方法的实现,则根据继承关系,向它的父类继续查找,
类方法的调用原则,当一个类方法被调用时,根据is a指针找到元类,元类首先会查找自身有没有该类方法的实现,如果没有,则元类会向它的父类查找该方法,这样可以一直找到继承链的头。
所以,为了保证父类的类方法在子类中被调用,所有子类的元类都会继承父类的元类,换而言之,类对象和元类对象有着同样的继承关系。
虽然有些东西被废弃了,但是原理是相通的,我们仍能从中获得一些有价值的信息。
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法缓存
例如,方法缓存。这里先说下相通的原理,后面会详细说道,优化后的缓存
cache 为方法调用的性能进行优化。通俗地讲,每当实例对象接收到一个消息时,它不会直接在isa指向的类的方法列表中遍历查找能够响应消息的方法,因为这样效率太低了,而是优先在Cache中查找。Runtime 系统会把被调用的方法存到Cache中(理论上讲一个方法如果被调用,那么它有可能今后还会被调用)如果cache没有,才去methodLists中查找方法。这样,对于那些经常用到的方法的调用,但提高了调用的效率