OC中的类与元类

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/WangErice/article/details/90698900

注:我们引用apple开源代码中objc4-750中的相关源代码定义。

OC中的对象是类的实例化出来的,这个我们都能理解。可是元类是什么,元类存在的意义是什么?

首先我们来看一下OC中关于对象的实现定义,

在对象结构体中居然只有一个Class类型的isa指针,那么对象的实例方法和成员变量放在哪里呢?因为只有一个isa指针,所以我们也只能从isa中去搜一下。下边我们看一下class的实现,

在这个结构体定义中,我们发现了父类的指针(super_class),发现了类的名称(name)以及其他描述信息(版本号version,类信息标志info,以及实例变量的大小描述instance_size)我们不难发现其实对象的所有成员变量(ivars),方法列表(methodLists),协议列表(protocols)都是存放在存放在类的定义中.而在实例对象进行方法调用时,对象会首先根据isa指针找到对应的类,然后从类的缓存(cache)中寻找方法实现,如果查找到方法实现则调用同时终止查找,如果没有则从methodLists中继续查找,如果找到则调用同时将该方法加入cache中并终止查找;如果没有发现,则顺着super_class指针继续遍历父类的methodLists,找到则调用同时将方法加入当前类的cache中并终止查找,依次递归直至找到方法实现或者直至父类为nil。 这种将对象方法保存在类中的做法,使得通过同一个类实例化的对象共享了同一份实例方法的实现,对象本身只需要保存自己的独立的数据就好了,既节约了存储空间又极大提升了运行的速度。

那么问题来了,我们在上边类的结构中并没有发现方法有区分实例方法和类方法,那么类的方法保存在哪里,调用类方法是又是如何查找实现的呢?

是不是觉得漏掉了啥?是的,我们发现在实例对象对应的类中还有一个跟实例对象一模一样的isa指针,那这个指针会是干嘛的呢?如果类方法保存在当前的类中,那么继承自同一个类的子类就必须要具有多分类方法的拷贝,不仅占据了大量的存储空间,也会给方法实现的查找带来巨大的额外开销。所以类方法的存储要么在super_class指向空间,要么就在isa指向的空间。而如果在super_class指向空间的话,我们会面临同样的问题,需要单独开辟空间来存储类方法和实例方法的标记,而每次调用都会陷入首先区分类方法还是实例方法,然后再去找遍历对应的方法列表,而事实上在上述类的实现中我们也并没有发现关于类方和实例方法的区分标志。那如果是像实例对象一样,类方法的实现保存在另外一个独立的地方,而所有的子类共享了一份类方法的实现,这样是不是就可以把对象方法的寻址方式和类方法的寻址方式统一起来了?所以类中的isa指针的作用就出现了,而类对象isa指针指向的空间地址,就是我们抽象出来的元类。

根据我们上边的理解,类方法的调用就会和实例对象的方法寻址完美统一起来。当类调用类方法时,首先根据类的isa找到类对应的元类,然后在元类的cache中查找方法的实现,如果查找到就调用该方法,如果查找不到则从当前类methodLists中去查找方法实现,如果未找到则继续沿着super_class查找去寻找。元类也有父类,类方法的查找也可以沿着继承链去查找方法的实现。这样类方法的寻址和实例对象的寻址就可以按照同样的寻址方式去查找,也就不用对类方法和实例方法做区分对待。实现了方法的不区分寻址,在很大程度上简化了寻址流程,提升了方法执行的速度。

于是乎,我们就再来看看这在网上已经被多次引用到的实例对象,类对象和元类对象的关系图。

从这张关系图我们可以看出如下结论:

(1)实例对象的isa指针指向了初始化该对象的类,而该对象的实例方法就保存在这个类的继承链中;

(2)类对象的isa指针指向了该类的元类,类方法存在与该元类的继承链中;

(3)元类与普通类一样,也有父类,也具备自己的继承关系链;

(4)元类的super_class指向了自己的父类,而isa 指向了最原始的元类;

(5)最原始的元类指向OC的Root class(NSObject),而Root class的isa却指向了最原始的元类。

下边我们来做个简单的验证,首先我们定义Person继承自NSObject,Student继承自Person

(1)使用object_getClass方法获取isa的指向,然后使用如下代码验证Person->isa与Student->isa->superClass是否指向同一内存:

从验证中,可以看出Person->isa和Student->isa->superClass是指向同一块对象空间的,同时Person->isa,student->isa和student->isa->superClass都是元类,而Person和Student却不是;

而且我们发现实例类所对应的元类的名称是和实例类相同的,也就是说实例类生成了同名的元类。

(2)如何获取一个类的全部类方法和实例方法?

根据上边的理论,实例方法保存在生成实例的类对象空间,而类方法则保存在类对应的元类对象空间,我们对刚才的Person和Student类做如下动

同时生成NSObject的MethodList分类,通过遍历获取到全部的类方法和实例方法,注意最初的class是如何获取的

然后通过打印对应的信息就可以验证我们的结论。

欢迎留言评论指正交流

猜你喜欢

转载自blog.csdn.net/WangErice/article/details/90698900