緣由:一般說(shuō)來(lái)如果想用一個(gè)系統(tǒng)的類,但是該類又不滿足需求,首先肯定是繼承自系統(tǒng)的類,重新構(gòu)建一個(gè)類,但這種方法,如果我們需求的類之比原來(lái)的類多一個(gè)屬性呢,繼承似乎又顯得比較麻煩,這種情況,使用Category就非常的方便了,但是Category又不能直接生成屬性,怎么辦呢,這個(gè)時(shí)候也許用runtime的關(guān)聯(lián)對(duì)象就可以辦到了。
先看一下面兩個(gè)方法
- 設(shè)置關(guān)聯(lián)對(duì)象
@param object 需要添加關(guān)聯(lián)的對(duì)象
@param key 添加的唯一標(biāo)識(shí)符
@param value 關(guān)聯(lián)的對(duì)
@param policy 關(guān)聯(lián)的策略,是個(gè)枚舉
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
- 獲得關(guān)聯(lián)對(duì)象
@param object 添加過(guò)關(guān)聯(lián)的對(duì)象
@param key 添加的唯一標(biāo)識(shí)符
objc_getAssociatedObject(id object, const void *key)
下面以** UITapGestureRecognizer**為例進(jìn)行舉例
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef void (^PQTapBlock)(UITapGestureRecognizer *);
@interface UITapGestureRecognizer (PQBlock)
@property (nonatomic, copy) PQTapBlock tapBlock;
- (instancetype)initWithTapBlock:(void (^)(UITapGestureRecognizer *tap))tapBlock;
- (void)handleTapGesture:(void (^)(UITapGestureRecognizer *tap))tapBlock;
@end
NS_ASSUME_NONNULL_END
#import "UITapGestureRecognizer+PQBlock.h"
#import <objc/runtime.h>
static const char *PQTapBlockKey = "PQTapBlockKey";
@implementation UITapGestureRecognizer (PQBlock)
//init
- (instancetype)initWithTapBlock:(void (^)(UITapGestureRecognizer *tap))tapBlock {
self = [super init];
if (self) {
[self addTarget:self action:@selector(tap:)];
self.tapBlock = [tapBlock copy];
}
return self;
}
// Handle
- (void)handleTapGesture:(void (^)(UITapGestureRecognizer *))tapBlock {
[self addTarget:self action:@selector(tap:)];
self.tapBlock = [tapBlock copy];
}
// SEL
- (void)tap:(UITapGestureRecognizer *)tap {
if (self.tapBlock) {
self.tapBlock(tap);
}
}
// Get
- (PQTapBlock)tapBlock {
return objc_getAssociatedObject(self, PQTapBlockKey);
}
// Set
- (void)setTapBlock:(PQTapBlock)tapBlock {
objc_setAssociatedObject(self, PQTapBlockKey, tapBlock, OBJC_ASSOCIATION_COPY);
}
具體可以嘗試兩種用法測(cè)試
init
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTapBlock:^(UITapGestureRecognizer *tap){
NSLog(@"點(diǎn)擊事件");
}];
[self.view addGestureRecognizer:tap];
handle
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
[tap handleTapGesture:^(UITapGestureRecognizer *tap){
NSLog(@"點(diǎn)擊事件");
}];
[self.view addGestureRecognizer:tap];
另外我們也可能注意到此處還有一個(gè)方法沒(méi)用到。
OBJC_EXPORT void objc_removeAssociatedObjects(id object)
但是我們不應(yīng)該自己手動(dòng)調(diào)用這個(gè)函數(shù)。此函數(shù)的主要目的是在“初試狀態(tài)”時(shí)方便地返回一個(gè)對(duì)象。你不應(yīng)該用這個(gè)函數(shù)來(lái)刪除對(duì)象的屬性,因?yàn)榭赡軙?huì)導(dǎo)致其他客戶對(duì)其添加的屬性也被移除了。規(guī)范的方法是:調(diào)用objc_setAssociatedObject 方法并傳入一個(gè)nil 值來(lái)清除一個(gè)關(guān)聯(lián)。同時(shí)此處再看看下面這個(gè)枚舉策略加深理解。
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, //指定一個(gè)關(guān)聯(lián)對(duì)象的弱引用。
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //指定一個(gè)關(guān)聯(lián)對(duì)象的強(qiáng)引用,不能被原子化使用。
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, //指定一個(gè)關(guān)聯(lián)對(duì)象的copy引用,不能被原子化使用
OBJC_ASSOCIATION_RETAIN = 01401, //指定一個(gè)關(guān)聯(lián)對(duì)象的強(qiáng)引用,能被原子化使用。
OBJC_ASSOCIATION_COPY = 01403, //指定一個(gè)關(guān)聯(lián)對(duì)象的copy引用,能被原子化使用。
};
同時(shí)注意,下面這三種效果是一樣的
//利用靜態(tài)變量地址唯一不變的特性
1、static void *strKey = &strKey;
2、static NSString *strKey = @"strKey";
3、static char strKey;
為了更好理解此處,推薦看下面這文章
Objective-C Associated Objects 的實(shí)現(xiàn)原理
以及iOS-運(yùn)行時(shí)(關(guān)聯(lián)詳解實(shí)例) 這個(gè)例子加深理解。