OC學習之Runtime之關聯物件
阿新 • • 發佈:2019-02-05
堅持 成長 每日一篇
前言
在Runtime中有一個我們經常忽略的特性就是關聯物件特性。
關聯物件類似於成員變數,不同的是關聯物件是在類建立例項後新增進去的。所以不能通過Runtime的獲取成員變數的方法獲取相關資訊。
注意:對關聯函式的操作只能通過下面的C函式進行操作。
// 設定關聯物件
void objc_setAssociatedObject ( id object, const void *key, id value, objc_AssociationPolicy policy );
// 獲取關聯物件
id objc_getAssociatedObject ( id object, const void *key );
//移除關聯物件
void objc_removeAssociatedObjects ( id object );
在對於系統的類如果我們想個類新增屬性,由於我們不能給類別裡面新增成員變數,只能通過建立該類的子類來實現。
Objective-C針對這一問題,提供了一個解決方案:即關聯物件(Associated Object)。
我們可以把關聯物件想象成一個Objective-C物件(如字典),這個物件通過給定的key連線到類的一個例項上。不過由於使用的是C介面,所以key是一個void指標(const void *)。我們還需要指定一個記憶體管理策略,以告訴Runtime如何管理這個物件的記憶體。這個記憶體管理的策略可以由以下值指定:
enum {
OBJC_ASSOCIATION_ASSIGN = 0, //弱引用,物件銷燬不會造成關聯物件的引用計數變化
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //強引用,不支援多執行緒安全
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, //深拷貝,不支援多執行緒安全
OBJC_ASSOCIATION_RETAIN = 01401, //強引用支援執行緒安全
OBJC_ASSOCIATION_COPY = 01403 //深拷貝,支援執行緒安全
};
例子
建立一個UIView的分類,我們要給分類新增一個單擊手勢回撥程式碼塊。
UIView+Tap.h
#import <UIKit/UIKit.h>
@interface UIView (Tap)
- (void)setTapActionWithBlock:(void (^)(void))block;
@end
UIView+Tap.m
#import "UIView+Tap.h"
#import <objc/runtime.h>
#define keyForAction "action"
#define keyForRecognizer "Recognizer"
@implementation UIView (Tap)
- (void)setTapActionWithBlock:(void (^)(void))block
{
//如果這裡不使用關聯物件,你可能需要不停移除手勢,然後再建立手勢
UITapGestureRecognizer *gesture = objc_getAssociatedObject(self,keyForRecognizer);
if (!gesture)
{
gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(actionForTapGesture:)];
[self addGestureRecognizer:gesture];
objc_setAssociatedObject(self, keyForRecognizer, gesture, OBJC_ASSOCIATION_RETAIN);
}
//這裡可以成功的新增一個關聯物件給後面的手勢回撥函式使用,而不需要通過全域性變數或成員變數。
objc_setAssociatedObject(self, keyForAction, block, OBJC_ASSOCIATION_COPY);
}
//實現回撥函式
- (void)actionForTapGesture:(UITapGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateRecognized)
{
//我們只能通過objc_getAssociatedObject來取出關聯物件
void(^action)(void) = objc_getAssociatedObject(self, keyForAction);
if (action)
{
action();
}
}
}
@end
在控制器裡面測試
- (void)viewDidLoad {
[super viewDidLoad];
//這樣實現了不需要建立分類,很好的拓展了已有的UIView類。
[self.view setTapActionWithBlock:^{
NSLog(@"is tap");
}];
}
執行程式我們就會看到當我們點選控制檢視時候輸出
2015-09-15 09:59:21.869 test[5410:381831] is tap
2015-09-15 09:59:23.005 test[5410:381831] is tap
2015-09-15 09:59:23.545 test[5410:381831] is tap
總結:關聯物件是一個非常實用的Runtime特性。可以很好的幫我們解決開發中遇到很多如傳值,擴充套件物件等問題。