0%

Runtime:Category

如果原来的类和分类中有相同的方法,那么最终执行的是分类方法。

编译完,每个分类都会生成一个category_t结构体,里面存储名称、对象方法列表、类方法列表、协议方法列表、属性列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
};

在合并分类的时候,其方法列表等不会覆盖原来类中的方法,是共存的。但分类的方法在前面,原来类的方法在后面,调用的时候,就会调用分类中的方法,如果多个分类的相同方法,后编译的分类会被调用。

如果想要执行被“覆盖”的类定义方法,可以逆序遍历方法列表,第一次取得的就是类定义的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)foo{   
[类 invokeOriginalMethod:self selector:_cmd];
}

+ (void)invokeOriginalMethod:(id)target selector:(SEL)selector {
uint count;
Method *list = class_copyMethodList([target class], &count);
for ( int i = count - 1 ; i >= 0; i--) {
Method method = list[i];
SEL name = method_getName(method);
IMP imp = method_getImplementation(method);
if (name == selector) {
((void (*)(id, SEL))imp)(target, name);
break;
}
}
free(list);
}

类对象/元类对象才是最终存储分类实例/类方法、属性、协议的地方。

扩展问题

Category的原本使用场景

区分不同的功能模块,使用分类单独实现。

Category的实现原理

分类编译后是category_t结构体,里面存储着分类的对象方法、类方法、属性、协议信息,在程序运行时,Runtime会把分类的数据合并到类信息(类对象、元类对象)中。

Category与Extension的区别

Extension在编译的时候,其数据已经包含在类信息中。Category在运行时才会把数据合并到类信息中。

Category为什么不能添加成员变量

category_t结构体只能存储属性,但没有存储objc_ivar_list结构体,没有用存储成员变量的地方,所以不能添加成员变量。

+load方法的执行

Runtime在加载类和分类的时候,会调用所有的+load方法,即使没有该类还没使用。

调用方式:函数地址直接调用。

调用时机:加载类和分类时调用一次,只会调用一次。

+load方法调用顺序:

  1. 调用类的+load
    • 按照编译顺序进行;
    • 先调父类,再调子类;
  2. 按照编译顺序调用分类的+load方法。

先去调用类的+load方法,若有父类则先调用父类的+load方法,再去调用分类的+load方法。

+initialize方法的执行

+initialize需要在使用(调用方法)类的时候才会调用。其调用顺序跟普通方法一致,即若有分类实现的+initialize方法,则调用分类的方法。

调用方式:objc_msgSend调用。

调用时机:在类第一次接收到消息时调用,所以父类可能会执行多次(只有父类实现了+initialize方法,而子类没有实现)。

参考

欢迎关注我的其它发布渠道