Category
2020-11-20 本文已影响0人
Mr_Shaozj
1. 分类编译之后的底层结构
/**
每一个分类,都会生成一个_category_t 的结构体
*/
struct _category_t {
//类名
const char *name;
//类对象
struct _class_t *cls;
//对象方法
const struct _method_list_t *instance_methods;
//类方法
const struct _method_list_t *class_methods;
//协议列表
const struct _protocol_list_t *protocols;
//属性列表
const struct _prop_list_t *properties;
};
2. 分类的加载处理过程
- 通过Runtime加载某个类的所有Category数据
- 把所有Category的方法,属性、协议数据、合并到一个大数组中,后面参与编译的Category数据,会在数组的前面。
- 在运行时, 将合并后的分类数据(方法、属性、协议),插入到类原来的数据前面。
3. 分类中的 + load方法
- +load方法会在runtime加载类、分类的时候调用
- 每个类、分类的load方法,在程序运行过程中,只会执行一次。
- 调用顺序
- 1 . 先调用类的load方法。子类的load方法调用前,会先调用父类的load方法。
- 在调用分类中的load方法。
- 都是按照编译的先后顺序调用。
- load方法都是通过函数的地址直接调用,并不是通过objc_msgSend函数调用。
4. initialize
- 会在类第一次接收到消息的时候调用。它是通过objc_msgSend() 实现的调用。
- 调用顺序
- 先调用父类的initialize,再去调用子类的initialize。
- 如果子类没有实现initialize,会调动父类的initialize,(存在父类被调用多次的情况)
- 如果分类实现了 initialize,就会覆盖类本身的initialize的调用。
5. Category的成员变量
- Category是不可以直接添加成员变量。
- 间接添加成员变量的方法
- 通过全局的变量进行引用
@interface Person (Eat)
@property(nonatomic,copy)NSString * foodName;
@end
NSString * foodName_;
@implementation Person (Eat)
-(void)setFoodName:(NSString *)foodName{
foodName_ = foodName;
}
- (NSString *)foodName{
return foodName_;
}
该方案的问题
因为是全局的变量,会导致不同的对象,修改相同的变量时,取值错误。
Person * p1 = [[Person alloc] init];
p1.foodName = @"p1-- foodName";
NSLog(@"%@",p1.foodName);
Person * p2 = [[Person alloc] init];
p2.foodName = @"p2-- foodName";
NSLog(@"%@",p1.foodName);
结果会成为 p2-- foodName
- 通过字典的方式
NSMutableDictionary * personEatDic;
@implementation Person (Eat)
+(void)load{
personEatDic = [NSMutableDictionary dictionaryWithCapacity:1];
}
-(void)setFoodName:(NSString *)foodName{
NSString * selfIsa = [NSString stringWithFormat:@"%p",self];
personEatDic[selfIsa] = foodName;
}
- (NSString *)foodName{
NSString * selfIsa = [NSString stringWithFormat:@"%p",self];
return personEatDic[selfIsa];
}
该方案的不足
每次添加一个属性,就需要全局声明一个字段来进行存储。
- 关联对象的方法 runtime
//以当前的内存地址作为key, static 仅限本类的.m 文件可以访问
static const void * MYPersonKey = &MYPersonKey;
@implementation Person (Eat)
-(void)setFoodName:(NSString *)foodName{
//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, MYPersonKey, foodName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)foodName{
return objc_getAssociatedObject(self, MYPersonKey);
}
----------------------------------------------------------------------------------------
//关联对象的全局变量的优化
//为考虑内存,仅需占用一个字节
static const char MYPersonKey;
-(void)setFoodName:(NSString *)foodName{
// &MYPersonKey 存储一个char字符的地址
objc_setAssociatedObject(self, &MYPersonKey, foodName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)foodName{
return objc_getAssociatedObject(self, &MYPersonKey);
}
6.关联对象的原理
objc_setAssociatedObject(<#id _Nonnull object#>, <#const void * _Nonnull key#>, <#id _Nullable value#>, <#objc_AssociationPolicy policy#>)
涉及的主要类
AssociationsManager
AssociationsHashMap
AssociationsMap
ObjectAssociation
关联对象的原理