简介
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/