该文为分类原理的简单记录,总结自如下文章,感谢作者分享:
1、分类的结构
分类的结构体如下
struct_category_t{
constchar *name;//类名
struct_class_t *cls;//分类所属的类
//category中所有给类添加的实例方法的列表(instanceMethods)
conststruct_method_list_t *instance_methods;
//category中所有添加的类方法的列表(classMethods)
conststruct_method_list_t *class_methods;
//category实现的所有协议的列表(protocols)
conststruct_protocol_list_t*protocols;
//category中添加的所有属性(instanceProperties)
conststruct_prop_list_t*properties;
};
2、分类的编译过程
通过如下命令将分类的m文件进行转换,分析其编译过程
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc xxx+xxx.m
structcategory_t{
constchar *name;// 类名
classref_tcls; // 分类所属的类
// 实例方法列表
structmethod_list_t*instanceMethods;
// 类方法列表
structmethod_list_t*classMethods;
// 遵循的协议列表
structprotocol_list_t*protocols;
// 属性列表
structproperty_list_t*instanceProperties;
// 如果是元类,就返回类方法列表;否则返回实例方法列表
method_list_t*methodsForMeta(boolisMeta) {
if(isMeta) {
return classMethods;
}else{
return instanceMethods;
}
}
// 如果是元类,就返回 nil,因为元类没有属性;否则返回实例属性列表,关于分类的实例属性,下文阐述
property_list_t*propertiesForMeta(boolisMeta) {
if(isMeta) {
return nil;// 元类没有属性;
}else{
return instanceProperties;//实例属性
}
}
};
3、分类的实现原理:
由上可得,分类在编译过程中,会生成 类方法列表
、实例方法列表
、属性列表
等,但是却 没有 实例变量列表(_ivar_list_t)
,可对比分类所属类的编译结果看,分类所属类是存在实例变量列表
的。然后,再来对比 实例方法列表
,还能发现分类的 实例方法列表
中,并未对分类属性生成 getter/setter
方法。
所以,这就是为什么 分类不能添加属性
的原因。
4、分类的加载
- 分类是在 运行时 进行加载的,其加载调用栈如下:
_objc_init //runtime的初始化函数,进行初始化操作,注册了镜像状态改变时的回调函数
└── map_2_images //加锁并调用 map_images_nolock
└── map_images_nolock //完成所有 class 的注册、fixup等工作,还有初始化自动释放池、初始化 side table 等工作并在函数后端调用了 _read_images
└── _read_images //加载类、Protocol、Category,加载分类的代码就写在 _read_images 函数的尾部
_objc_init 函数在 objc-os.mm 中,_read_images 方法在objc-runtime-new.mm 中。
- 加载过程:
1、把分类的 实例方法
、属性
、协议
添加到类的实例对象中原本存储的 实例方法
、属性
、协议列表
的 前面 ;
2、把分类的 类方法
和协议
添加到类的元类
上。
如此,保证了分类方法 优先调用,注意,不是覆盖,而是共同存在在实例方法列表中,只是分类在前而已。
网友评论