Objective - C 关联对象(一) 如何添加"成员变量"

(一)分类 - 能否添加“成员变量”

  • 因为分类底层结构的限制,不能直接添加成员变量到分类中
  • 但可以通过关联对象间接实现

属性 != 变量 分类中添加属性,但并不会添加成员变量及对应的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:

  1. 添加关联对象 void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy)
  2. 获得关联对象 id objc_getAssociatedObject(id object, const void * key)
  3. 移除所有的关联对象 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 对应的修饰符

image.png

总结一下,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);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容