Associated Objects

简介

Associated Objects (对象关联)支持以键值对的形式动态地向对象添加,删除,获取关联值。

使用场景

按照 Mattt Thompson 大神的文章 Associated Objects 中的说法,Associated Objects 主要有以下三个使用场景:

1.添加私有属性用于更好地去实现细节
2.添加公有属性增强Category的功能
3.为KVO创建一个关联的观察者

这里引用文章中的示例代码,看不懂也没关系后面我会再细说:

//NSObject+AssociatedObject.h
@interface NSObject (AssociatedObject) 
@property (nonatomic, strong) id associatedObject; 
@end 
//NSObject+AssociatedObject.m
#import "NSObject+AssociatedObject.h"
#import <objc/runtime.h>
@implementation NSObject (AssociatedObject) 
@dynamic associatedObject; 
 
- (void)setAssociatedObject:(id)object { 
     objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
} 
 
- (id)associatedObject { 
    return objc_getAssociatedObject(self, @selector(associatedObject)); 
} 

这下原来无法添加实例变量的Category终于扬眉吐气了。

使用方法

先来了解下<objc/runtime.h>中对应的三个方法

// 设置关联对象,value传入nil来清除关联对象
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
// 获取关联对象
id objc_getAssociatedObject(id object, const void *key);
// 清空所有关联的对象(包括其他Client添加的),所以不应该直接调用该方法
void objc_removeAssociatedObjects(id object);

key的选择

需要注意的是,方法中用到的key通常来说该属性应该是常量、唯一的、在适用范围内用getter和setter访问到的,有三种推荐使用的方式:

//1.使用char
static char kAssociatedObjectKey;
objc_getAssociatedObject(self, &kAssociatedObjectKey);
//2.使用指针
static void *kAssociatedObjectKey = &kAssociatedObjectKey;
objc_getAssociatedObject(self, kAssociatedObjectKey);
//3.使用selector
objc_getAssociatedObject(self, _cmd);
objc_setAssociatedObject(self, @selector(xxx),xxx,OBJC_ASSOCIATION_RETAIN_NONATOMIC);

关联特性的选择

另外一个需要注意的是objc_AssociationPolicy的类型特性:

行为 @property
OBJC_ASSOCIATION_ASSIGN @property (assign)、@property (unsafe_unretained)
OBJC_ASSOCIATION_RETAIN_NONATOMIC @property (nonatomic, strong)
OBJC_ASSOCIATION_COPY_NONATOMIC @property (nonatomic, copy)
OBJC_ASSOCIATION_RETAIN @property (atomic, strong)
OBJC_ASSOCIATION_COPY @property (atomic, copy)

实际应用

大家对UIGestureRecognizer的用法应该是再熟悉不过了

UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(invoke:)];
[tap addTarget:self action:@selector(invokeXX:)];
[self.view addGestureRecognizer:tap];

接着我拿ibireme大神 YYCategories 中的一段代码来演示UIGestureRecognizer的文艺用法:

UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithActionBlock:^(id  _Nonnull sender) {
      //do something...
}];
[tap addActionBlock:^(id  _Nonnull sender) {
       //do something...
}];
[self.view addGestureRecognizer:tap];

UITapGestureRecognizer可以直接使用block来操作了。虽然只是使用风格上的问题,但对于命名纠结症的人来说是一大福音。

接下来,我们再探究下UIGestureRecognizer+YYAdd里的宝藏

//UIGestureRecognizer+YYAdd.h
NS_ASSUME_NONNULL_BEGIN //在该范围内传入nil会给予警告
...
//初始化一个带block的 gesture-recognizer 对象
- (instancetype)initWithActionBlock:(void (^)(id sender))block;
//给gesture-recognizer对象追加一个block
- (void)addActionBlock:(void (^)(id sender))block;
//删除所有block
- (void)removeAllActionBlocks;
...
NS_ASSUME_NONNULL_END
//UIGestureRecognizer+YYAdd.m
static const int block_key; //< key值
...
- (instancetype)initWithActionBlock:(void (^)(id sender))block {
    self = [self init];
    [self addActionBlock:block];
    return self;
}

- (void)addActionBlock:(void (^)(id sender))block {
    _YYUIGestureRecognizerBlockTarget *target = [[_YYUIGestureRecognizerBlockTarget alloc] initWithBlock:block]; //< 初始化一个对象用于存储block
    [self addTarget:target action:@selector(invoke:)];
    NSMutableArray *targets = [self _yy_allUIGestureRecognizerBlockTargets];
    [targets addObject:target]; //< 将持有block的对象放入数组中,方便管理
}

- (void)removeAllActionBlocks{ //< 清除所有绑定的target-action和block数组
    NSMutableArray *targets = [self _yy_allUIGestureRecognizerBlockTargets];
    [targets enumerateObjectsUsingBlock:^(id target, NSUInteger idx, BOOL *stop) {
        [self removeTarget:target action:@selector(invoke:)];
    }];
    [targets removeAllObjects];
}

- (NSMutableArray *)_yy_allUIGestureRecognizerBlockTargets {
    NSMutableArray *targets = objc_getAssociatedObject(self, &block_key); //< 从关联对象中取得持有block对象的数组
    if (!targets) { //< 没有的话就初始化一个并通过关联对象动态添加
        targets = [NSMutableArray array];
        objc_setAssociatedObject(self, &block_key, targets, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return targets;
}

参考链接

http://nshipster.com/associated-objects/
http://blog.leichunfeng.com/blog/2015/06/26/objective-c-associated-objects-implementation-principle/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,784评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,745评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,702评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,229评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,245评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,376评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,798评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,471评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,655评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,485评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,535评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,235评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,793评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,863评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,096评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,654评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,233评论 2 341

推荐阅读更多精彩内容