runtime 關(guān)聯(lián)對(duì)象

緣由:一般說(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è)例子加深理解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容