Category 的结构引申
附加:Category 的处理过程
Xcode在编译时,会将所有参与编译的分类转变成一个c++结构体(图1所示)
图片 1.png- 运行时通过runtime加载所有category数据,并将其
方法、属性、协议
数据按照编译顺序
,分别合并到
一个对应的大数组
中
[后编译的在数组的前面
] ----> 这样方法调用时就会优先调用后编译的分类方法
- 将合并后的分类数据(方法、属性、协议),插入到类(
类对象、元类对象
)原来数据的前面
---->优先调用分类方法
综上概括:
- 本质上所有
对象方法、协议、属性
最终都会按照分类在前,原类在后;后编译的分类在前,先编译在后
的原则,分别存放在类对象
的对象方法、协议、属性
对应的数组里面- 所有
类方法
最终都会按照分类在前,原类在后;后编译的分类在前,先编译在后
的原则,存放在元类对象
的类方法
数组里面
结论
- 最终方法或属性等都一个数组,且有序,所以就有了方法调用顺序:
后编译的分类方法-->先编译的分类方法-->类对象(元类对象)原来的方法
- 因为程序运行时runtime是把所有分类数据合并到原类所有数据中,所以
不管有没有import
某个分类的头文件
,这个分类中重写的方法
都会可能被调用- 分类结构体中
只
包含有属性数组``struct property_list *instanceProperties;
,所以分类允许添加属性
的,但需要手动实现getter 和setter(@proterty... 在分类声明属性,只会生成属性的getter 和setter声明,但不会实现,和生成带
_的成员变量
)- 分类结构体中不含有
成员变量数组``struct property_list *instanceProperties;
,可见分类不存在成员变量
,也不能添加成员变量
拓展:如何给分类添加伪
成员变量
- 上述
结论
中谈到:由于分类结构限制,分类不存在成员变量
,也不能添加成员变量
,但是我们可以用伪成员变量
实现类似效果的
- 实现方案:
a. 在分类interface
用中@property
声明一个属性
b. 在分类implementation
中实现属性的getter 和setter
。由于分类不能添加和生成成员变量,所以最常用且可靠的方案是通过Runtime API
关联一个伪成员变量
,来实现getter 和setter
方法
- 关联对象的相关
Runtime API
// 添加关联对象
void objc_setAssociatedObject(id object,
const void * key,
id value,
objc_AssociationPolicy policy)
// 获得关联对象
id objc_getAssociatedObject(id object, const void * key)
// 移除所有的关联对象
void objc_removeAssociatedObjects(id object)
/** 参数说明:
* object :id类型, 一般都传 self;用途相当于一个字典的Key,
用来归类所关联的成员变量;
* key :const void * 类型指针,相当于读写属性值的Key,
可以传用@selector(getter) --》 [这样能免除繁琐的宏或常量定义]
* value : 成员变量的值 (对象)
* objc_AssociationPolicy policy : 内存策略,相当于retain、strong、copy等
- 示例
#import <objc/runtime.h>
@interface TLPerson (ExampleCode)
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic) int weight;
@end
@implementation TLPerson (ExampleCode)
- (void)setName:(NSString *)name
{
/* policy 参数类型
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
OBJC_ASSOCIATION_RETAIN = 01401,
OBJC_ASSOCIATION_COPY = 01403
};*/
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name
{
// 隐式参数
// _cmd == @selector(name),
// 用@selector(name)作为key,可以简化代码,省去一些宏定义,还有能有代码提示
return objc_getAssociatedObject(self, _cmd);
}
- (void)setWeight:(int)weight
{
objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (int)weight
{
// _cmd == @selector(weight)
return [objc_getAssociatedObject(self, _cmd) intValue];
}
@end