iOS runtime實(shí)戰(zhàn)應(yīng)用:關(guān)聯(lián)對(duì)象

前言

在開(kāi)始之前建議先閱讀iOS runtime的基礎(chǔ)理解篇:iOS內(nèi)功篇:runtime

有筒子在面試的時(shí)候,遇到這樣一個(gè)問(wèn)題:“如何給NSArray添加一個(gè)屬性(不能使用繼承)”,筒子立馬蒙逼了,不能用繼承,難道用分類(lèi)?但是分類(lèi)貌似只能添加方法不能添加屬性啊,筒子百思不得其解,直到后來(lái)接觸到了runtime才恍然大悟。

什么是關(guān)聯(lián)對(duì)象

關(guān)聯(lián)對(duì)象是指某個(gè)OC對(duì)象通過(guò)一個(gè)唯一的key連接到一個(gè)類(lèi)的實(shí)例上。
舉個(gè)例子:xiaoming是Person類(lèi)的一個(gè)實(shí)例,他的dog(一個(gè)OC對(duì)象)通過(guò)一根繩子(key)被他牽著散步,這可以說(shuō)xiaoming和dog是關(guān)聯(lián)起來(lái)的,當(dāng)然xiaoming可以牽著多個(gè)dog。

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

runtime提供給我們的方法:

//關(guān)聯(lián)對(duì)象
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
//獲取關(guān)聯(lián)的對(duì)象
id objc_getAssociatedObject(id object, const void *key)
//移除關(guān)聯(lián)的對(duì)象
void objc_removeAssociatedObjects(id object)

變量說(shuō)明:

id object:被關(guān)聯(lián)的對(duì)象(如xiaoming)
const void *key:關(guān)聯(lián)的key,要求唯一
id value:關(guān)聯(lián)的對(duì)象(如dog)
objc_AssociationPolicy policy:內(nèi)存管理的策略

objc_AssociationPolicy policy的enum值有:

OBJC_ASSOCIATION_ASSIGN = 0,          
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, 
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  
OBJC_ASSOCIATION_RETAIN = 01401,       
OBJC_ASSOCIATION_COPY = 01403   

當(dāng)對(duì)象被釋放時(shí),會(huì)根據(jù)這個(gè)策略來(lái)決定是否釋放關(guān)聯(lián)的對(duì)象,當(dāng)策略是RETAIN/COPY時(shí),會(huì)釋放(release)關(guān)聯(lián)的對(duì)象,當(dāng)是ASSIGN,將不會(huì)釋放。
值得注意的是,我們不需要主動(dòng)調(diào)用removeAssociated來(lái)接觸關(guān)聯(lián)的對(duì)象,如果需要解除指定的對(duì)象,可以使用setAssociatedObject置nil來(lái)實(shí)現(xiàn)。

關(guān)聯(lián)對(duì)象的應(yīng)用

1、添加公共屬性

這是最常用的一個(gè)模式,通常我們會(huì)在類(lèi)聲明里面添加屬性,但是出于某些需求(如前言描述的情況),我們需要在分類(lèi)里添加一個(gè)或多個(gè)屬性的話,編譯器就會(huì)報(bào)錯(cuò),這個(gè)問(wèn)題的解決方案就是使用runtime的關(guān)聯(lián)對(duì)象。
應(yīng)用舉例:
我們需要自定義一個(gè)tabbar,并暴露公共的屬性和方法。(讀者們可以思考下使用繼承和分類(lèi)實(shí)現(xiàn)的優(yōu)點(diǎn)和不足之處)

@interface UITabBarController (Custom)

@property (nonatomic, strong) SUCustomTabbar * customTabbar;

@end
#import "UITabBarController+Custom.h"
#import <objc/runtime.h>

@implementation UITabBarController (Custom)

- (void)setCustomTabbar:(UIView *)customTabbar {
    //這里使用方法的指針地址作為唯一的key
    objc_setAssociatedObject(self, @selector(customTabbar), customTabbar, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIView *)customTabbar {
    return objc_getAssociatedObject(self, @selector(customTabbar));
}

//其他方法...

@end

這樣,我們就可以像原生的tabbar一樣使用自定義的tabbar:

[self.tabBarController.customTabbar doSomgthig];

2、添加私有成員變量

有時(shí)候,需要在分類(lèi)中添加不想暴露在公共聲明的成員變量。
應(yīng)用舉例:給按鈕添加點(diǎn)擊時(shí)間的回調(diào)

@interface UIButton (Callback)

- (instancetype)initWithFrame:(CGRect)frame callback:(void (^)(UIButton *))callbackBlock;

@end
@interface UIButton ()

@property (nonatomic, copy) void (^callbackBlock)(UIButton * button);

@end

@implementation UIButton (Callback)

- (void (^)(UIButton *))callbackBlock {
    return objc_getAssociatedObject(self, @selector(callbackBlock));
}

- (void)setCallbackBlock:(void (^)(UIButton *))callbackBlock {
    objc_setAssociatedObject(self, @selector(callbackBlock), callbackBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (instancetype)initWithFrame:(CGRect)frame callback:(void (^)(UIButton *))callbackBlock {
    
    if (self = [super initWithFrame:frame]) {
        self.callbackBlock = callbackBlock;
        [self addTarget:self action:@selector(didClickAction:) forControlEvents:UIControlEventTouchUpInside];
    }
    return self;
}

- (void)didClickAction:(UIButton *)button {
    self.callbackBlock(button);
}

@end

讀者思考:以上代碼是否會(huì)存在內(nèi)存管理的問(wèn)題

3、關(guān)聯(lián)KVO觀察者
有時(shí)候我們?cè)诜诸?lèi)中使用KVO,推薦使用關(guān)聯(lián)的對(duì)象作為觀察者,盡量避免對(duì)象觀察自身。
此應(yīng)用模式不再舉例,有興趣的讀者可以自行深入研究,或者將代碼貼到評(píng)論處。

思考

1、關(guān)聯(lián)對(duì)象更多的應(yīng)用模式?
2、關(guān)聯(lián)對(duì)象是否是解決問(wèn)題的首選?
3、關(guān)聯(lián)對(duì)象的副作用?

歡迎大家交流探討。

最后編輯于
?著作權(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)容

  • 問(wèn)題:“如何給OC對(duì)象(如 NSArray、UIViewController等)添加一個(gè)屬性,且不使用繼承的情況下...
    O2Space_Xiu閱讀 863評(píng)論 0 0
  • 對(duì)于從事 iOS 開(kāi)發(fā)人員來(lái)說(shuō),所有的人都會(huì)答出【runtime 是運(yùn)行時(shí)】什么情況下用runtime?大部分人能...
    夢(mèng)夜繁星閱讀 3,806評(píng)論 7 64
  • 什么是關(guān)聯(lián)對(duì)象 關(guān)聯(lián)對(duì)象是指某個(gè)OC對(duì)象通過(guò)一個(gè)唯一的key連接到一個(gè)類(lèi)的實(shí)例上。 舉個(gè)例子:xiaoming是P...
    飄金閱讀 1,183評(píng)論 0 2
  • 周晨(大發(fā)化纖有限公司) 276期,感謝1組成員 【日精進(jìn)打卡第33天】 【知~學(xué)習(xí)】 《六項(xiàng)精進(jìn)》讀0遍 共70...
    周晨i閱讀 152評(píng)論 0 0
  • 有一天,爸爸坐在路邊休息,懷里抱著睡著了的孫女曙光,頭上的大草帽遮住了爺孫倆的頭臉。有個(gè)人走過(guò)來(lái),好奇的問(wèn)到,“大...
    Bernardxiao閱讀 217評(píng)論 0 2

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