底层原理 - Category
一、概述
Category是对类的扩展,可以添加一些常用方法,更加方便地调用;
二、Category的底层结构
定义在objc-runtime-new.h中
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);
};
三、Category的加载处理过程
1、通过Runtime加载某个类的所有Category数据;
2、把所有Category的方法、属性、协议数据,合并到一个大数组中(顺序:后面参与编译的Category数据,会在数组的前面);
3、将合并后的分类数据(方法、属性、协议)插入到类原来的数据前面;
四、+load方法 和 +initialize方法
1、调用时机
1.1、load调用时间:
- +load方法会在runtime加载类、分类时调用;
- 每个类,分类的+load,在程序运行过程中只调用一次; 1.2、initialize调用时机:
- +initialize方法会在类第一次接收到消息时调用;
- 每个类中的Initialize仅会初始化一次;
2、调用方式
2.1、load调用方式:
- load是根据函数地址直接调用; 2.2、initialize调用方式:
- initialize是通过
objc-msgSend调用;
3、调用顺序
3.1、load调用顺序:
3.1.1、先调用类的+load;
- 按照编译先后顺序调用(先编译,先调用);
- 调用子类的+load之前会调用父类的+load; 3.1.2、再调用分类的+load;
- 按照编译先后顺序调用(先编译,先调用);
+load方法是根据方法地址直接调用,并不是经过
objc_msgSend函数调用;
3.2、initialize调用顺序:
3.2.1、先调用父类的 +initialize方法;
3.2.2、再调用子类的 +initialize方法;
注意:在调用子类initialize时,若子类中没有,则会去父类中查找,这时有可能父类方法会出现多次调用的情况) 原文地址