前言
在開(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ì)象的副作用?
歡迎大家交流探討雏蛮。