(一)分类 - 能否添加“成员变量”
- 因为分类底层结构的限制,不能直接添加成员变量到分类中
- 但可以通过关联对象来间接实现
属性 != 变量 分类中添加属性,但并不会添加成员变量及对应的set、get方法
(1)如何获得类似以前成员变量的效果
方法一:定义全局变量 通过set、get方法存取
NSInteger weight_;
-(void)setWeight:(NSInteger)weight{
weight_ = weight;
}
-(NSInteger)weight{
return weight_;
}
缺点:多个对象共用一个全局变量
方法二:使用NSDictionary存取不同对象的值
NSMutableDictionary *weights_;
+(void)load{
weights_ = [NSMutableDictionary dictionary];
}
-(void)setWeight:(NSInteger)weight{
NSString *key = [NSString stringWithFormat: @"%p",self];//使用self的内存地址作key 保证唯一
weights_[key] = @(weight);
}
-(NSInteger)weight{
NSString *key = [NSString stringWithFormat: @"%p",self];
return [weights_[key] intValue];
}
缺点:1.虽然从外部无法看出区别,但是实际存储位置有区别 成员变量是存储在对象内部,而通过dictionary存储在全局字典对象 2.同时访问存在线程问题 不同的对象在不同的线程同时访问set方法时 3.添加多个变量时 需要定义多个去全局变量
(2)方法三:关联对象
这就是本章的重点,通过关联对象来添加“成员变量”
关联对象提供了以下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)
#import <objc/runtime.h>
#define ZQNameKey @"name"
//使用宏 更安全的保证set、get的使用的key一致
@implementation ZQPerson (Test)
static const char ZQName; //也可以使用static const NSString *ZQName = @"ZQNameKey";
static const char ZQHeight; //但是我们用char 更节约内存 且我们并不需要变量内容 只需要变量地址而已
-(void)setName:(NSString *)name{
objc_setAssociatedObject(self, &ZQName, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
//objc_setAssociatedObject(self, ZQNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
//方法二:也可以直接使用同一个字符串 因为字符串常量存储在常量区 地址也不会改变
//objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
//方法三:※推荐使用@selector(obj) 返回的是一个指向一个结构体的指针
//优点:1.可读性更高 2.编译器提示
}
-(NSString *)name{
return objc_getAssociatedObject(self, &ZQName);
//return objc_getAssociatedObject(self, ZQNameKey);
//return objc_getAssociatedObject(self, @selector(name));也能改成return objc_getAssociatedObject(self, _cmd);
//隐式参数 _cmd = @selector(name) set和get不可同时使用
// _cmd表示当前方法 因为key要一致 所以不能同时使用
}
-(void)setWeight:(NSInteger)weight{
objc_setAssociatedObject(self, &ZQHeight, @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSInteger)weight{
return [objc_getAssociatedObject(self, &ZQHeight) intValue];
}
(3)objc_AssociationPolicy 对应的修饰符
总结一下,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(set也可以,get更直观)
objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, @selector(getter))
通过上面多个方法对比,推荐使用方法:
//.h
@property(nonatomic,copy) NSString *name;
@property(nonatomic,assign) NSInteger weight;
@property(nonatomic,class,copy) NSString *sex;
//.m
//实例对象
-(void)setName:(NSString *)name{
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name{
return objc_getAssociatedObject(self, _cmd);
}
//类对象
+ (void)setSex:(NSString *)sex{
objc_setAssociatedObject([self class], @selector(sex), sex, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
+ (NSString *)sex{
return objc_getAssociatedObject([self class], _cmd);
}