在Objective-C中,几乎所有的类都是NSObject的子类,另一部分都是NSProxy的子类。
在Foundation框架下,NSObject和NSProxy两个基类定义了类层次结构中该类下方所有类的公共接口和行为。NSProxy是专门用于实现代理的类。这两个类都遵循了NSObject协议。在NSObject协议中,声明了所有OC对象的公共方法。
先从结构定义上来看
image.png image.png这里是NSObject的一些定义,直接在编译器里面commend点进NSObject就可以看到,虽然有一些是标注适用Objc2.0之前。但是并不是代表之后就不存在这些参数了,只是在结构上进一步的进行了封装。里面有一句“ Class instead of struct objc_class *”,所以我们可以再去找找有关objc_class的源码。
image.png这里看到objc_class 是继承自objc_object
image.pngimage.png
从上可以看出,我们平时用的id类型,其实就是一个objc_object对象,这也是为什么id类型可以指向任何对象的原因。
然后objc_class是继承自objc_object的,这表明其实类本质也是一个对象。
这里要引入一个问题我们在调用实例方法的时候,通过类的实例对象来进行调用。那调用类方法的时候,又是调用谁的实例呢?
先看看实例方法是如何别调用的,当对象的实例方法被调用,是通过isa来找到对应的类,然后在该类的class_data_bits_t中去查找方法。class_data_bits_t是指向了类对象的数据区域。在该数据区域内查找相应方法的对应实现。
对于类对象则是引入了一个元类的概念(meta-class)。
类对象的类方法调用时,通过类的 isa 在元类中获取方法的实现。它存储着一个类的所有类方法。每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同。
额外提两点,第一点是在运行的时候,实例是可以创建无数个的,但是类对象和元类对象却是唯一的。类对象和元类对象会在main方法执行之前就被创建出来。所以在工作的过程中对于废弃了的类尽量就直接移除掉。这样可以避免一些不必要的损耗。
第二点是对象的方法并没有存储于对象的结构体中,这个其实也比较合理,如果每创建一个都要把相同的方法复制一遍,这样对内存太不友好了。所以在实例方法被调用时,它要通过自己持有的 isa 来查找对应的类,然后在这里的 class_data_bits_t 结构体中查找对应方法的实现。同时,每一个 objc_class 也有一个指向自己的父类的指针 super_class 用来查找继承的方法。
当实例方法被调用时,它要通过自己持有的 isa 来查找对应的类
从网上找了一张NSObject的类图
image.png
这个就是现在的结构图
接下来具体介绍一下
cache_t cache
class_data_bits_t bits
cache_t cache的源码
image.png
这里面包含了一个bucket_t的结构体和两个mask_t参数
分别看看对应的源码
image.png image.png image.png
关于_mask和_occupied查资料是说
_mask:分配用来缓存bucket的总数。
_occupied:表明目前实际占用的缓存bucket的个数。
bucket_t的结构体中存储了一个参数与一个函数指针,这个函数指针是指向一个具体的方法实现。
cache的作用主要是用来缓存常用的方法,当一个方法被调用的时候,会优先在cache中查找,如果没找到再去methodLists中查找。
class_data_bits_t
对于这个,源码里面有一句注释 // class_rw_t * plus custom rr/alloc flags
相当于 class_rw_t指针加上 rr/alloc 的标志。
Objc的类的属性、方法、以及遵循的协议在obj 2.0的版本之后都放在class_rw_t中。class_ro_t是一个指向常量的指针,存储来编译器决定了的属性、方法和遵守协议。
网友评论