分类不能直接添加成员变量
前边两篇文章我们介绍了什么是Category。接下来我们看下如何给category添加成员变量
通过读取分类的源码我们知道
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; // 属性列表
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods
else return instanceMethods
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi)
}
可以为一个类的category添加对象,但是我们需要注意到。添加的属性只是声明,没有为这个类添加成员变量,也没有对这个属性实现Set和Get方法。
通过关联对象添加成员变量
有关关联对象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)
参数说明:
id object:需要关联的对象
const void * key:需要关联的键 这是一个指针类型
id value:就是需要关联的值与上方的key对应。传递nil以清除现有的关联
objc_AssociationPolicy policy:关联策略
关联策略对应的修饰符
我们创建一个Person类 和他的分类 Test
@interface DDPerson : NSObject
@end
#import "DDPerson.h"
@implementation DDPerson
@end
#import "DDPerson.h"
@interface DDPerson (Test)
@property (copy, nonatomic) NSString *name;
@property (asign, nonatomic) int weight;
@end
#import "DDPerson+Test.h"
#import <objc/runtime.h>
// 定义Name的key
static const char name_;
@implementation DDPerson (Test)
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self, &name_, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
return objc_getAssociatedObject(self, &name_);
}
- (void)setWeight:(int)weight
{
// 这里我们把weight的get方法当做key, 因为这个参数类型实际上是一个指针类型,所以任何指针类型的参数都可以当做key来使用
objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (int)weight
{
//获取关联对象的时候,我们用方法的隐式参数_cmd
// _cmd 参数是OC方法默认传入的参数,每个方法都会有这个参数。_cmd == @selector(weight) 这个跟setWeight时传入的key刚好对应起来
return [objc_getAssociatedObject(self, _cmd) intValue];
}
@end
关联对象的原理
关联对象的核心对象有四个,分别为
AssociationsManager,AssociationsHashMap,ObjectAssociationMap,ObjcAssociation
拓展
关联对象的常用key
static void *MyKey = &MyKey;
objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, MyKey)
static char MyKey;
objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, &MyKey)
// 使用属性名作为key
objc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_getAssociatedObject(obj, @"property");
// 使用get方法的@selecor作为key
objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, @selector(getter))