Category的底层介绍:
分类的底层结构:
struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods; // 对象方法列表
struct method_list_t *classMethods; // 类方法列表
struct protocol_list_t *protocols; // 协议列表
struct property_list_t *instanceProperties; // 实例属性列表
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties; // 类属性列表
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
在分类中,对象方法,类方法,协议,和属性都可以找到对应的存储方式。并且我们发现分类结构体中是不存在成员变量的,因此分类中是不允许添加成员变量的。分类中添加的属性并不会帮助我们自动生成成员变量,只会生成get set方法的声明,需要我们自己去实现。
Category的实现原理:
将category中的方法,属性,协议数据放在category_t结构体中,然后将结构体内的方法列表拷贝到类对象的方法列表中
Category为什么只能加方法不能加属性:
Category可以添加属性,但是并不会自动生成成员变量及set/get方法。因为category_t结构体中并不存在成员变量。
成员变量存放在实例对象中的,并且在编译的那一刻就已经决定好了,而分类是在运行时才去加载的,那么我们就无法在程序运行时将分类的成员变量添加到实例对象的结构体重,一次分类中不可以添加成员变量。
Category中有load方法吗?load方法是什么时候调用的?load 方法能继承吗?
Category中有load方法,load方法在程序启动装载类信息的时候就会调用。load方法可以继承。调用子类的load方法之前,会先调用父类的load方法
load、initialize的区别,以及它们在category重写的时候的调用的次序
1、区别在于调用方式和调用时刻
调用方式:load是根据函数地址直接调用,initialize是通过objc_msgSend(消息发送)调用
调用时刻:load是runtime加载类、分类的时候调用(只会调用1次),initialize是类第一次接收到消息的时候调用,每一个类只会initialize一次(父类的initialize方法可能会被调用多次)
2、调用顺序:
先调用类的load方法,先编译那个类,就先调用load。在调用load之前会先调用父类的load方法。分类中load方法不会覆盖本类的load方法,先编译的分类优先调用load方法
initialize先初始化父类,之后再初始化子类。如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次),如果分类实现了+initialize,就覆盖类本身的+initialize调用
网友评论