ios 中的關聯對象

   關聯對象的簡單使用:web

#import "wangmumu.h"
 
@interface wangmumu ()
{
 
}
 
static char staticString;
 
@implementation SHVCarsViewController
 
- ( void )viewDidLoad {
     [ super viewDidLoad];
     // Do any additional setup after loading the view.
     
     self .edgesForExtendedLayout= UIRectEdgeNone ;
     self .tableView.hidden = YES ;
     UIButton *btn = [[ UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
     btn.backgroundColor = [ UIColor grayColor];
     [btn addTarget: self action: @selector (btnn:) forControlEvents: UIControlEventTouchUpInside ];
     objc_setAssociatedObject(btn, &staticString, @ "aaaa" , OBJC_ASSOCIATION_RETAIN_NONATOMIC);
     [ self .view addSubview:btn];
     
     
}
 
- ( void )btnn:( UIButton *)btn{
 
     NSString *str = objc_getAssociatedObject(btn, &staticString);
     NSLog (@ "%@" ,str);
 
}
 
/*
objc_setAssociatedObject(btn, &staticString, @"aaaa", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
 
有四個參數
1.被關聯的對象
2.要關聯的對象的鍵值,通常設置成靜態的,用於獲取關聯對象的值
3.要傳得值 通常是靜態變量
4.關聯時採用的協議,有assign,retain,copy等協議 點擊進去選取枚舉
 
*/
 
 
 
 
 
 

關聯對象(Associated Object)

關聯對象是Runtime中一個很是實用的特性,不過可能很容易被忽視。多線程

關聯對象相似於成員變量,不過是在運行時添加的。咱們一般會把成員變量(Ivar)放在類聲明的頭文件中,或者放在類實現的@implementation後面。但這有一個缺點,咱們不能在分類中添加成員變量。若是咱們嘗試在分類中添加新的成員變量,編譯器會報錯。ide

咱們可能但願經過使用(甚至是濫用)全局變量來解決這個問題。但這些都不是Ivar,由於他們不會鏈接到一個單獨的實例。所以,這種方法不多使用。函數

Objective-C針對這一問題,提供了一個解決方案:即關聯對象(Associated Object)。編碼

咱們能夠把關聯對象想象成一個Objective-C對象(如字典),這個對象經過給定的key鏈接到類的一個實例上。不過因爲使用的是C接口,因此key是一個void指針(const void *)。咱們還須要指定一個內存管理策略,以告訴Runtime如何管理這個對象的內存。這個內存管理的策略能夠由如下值指定:spa

1
2
3
4
5
OBJC_ASSOCIATION_ASSIGN
OBJC_ASSOCIATION_RETAIN_NONATOMIC
OBJC_ASSOCIATION_COPY_NONATOMIC
OBJC_ASSOCIATION_RETAIN
OBJC_ASSOCIATION_COPY

當宿主對象被釋放時,會根據指定的內存管理策略來處理關聯對象。若是指定的策略是assign,則宿主釋放時,關聯對象不會被釋放;而若是指定的是retain或者是copy,則宿主釋放時,關聯對象會被釋放。咱們甚至能夠選擇是不是自動retain/copy。當咱們須要在多個線程中處理訪問關聯對象的多線程代碼時,這就很是有用了。.net

咱們將一個對象鏈接到其它對象所須要作的就是下面兩行代碼:線程

1
2
3
static char myKey;
 
objc_setAssociatedObject(self, &myKey, anObject, OBJC_ASSOCIATION_RETAIN);

在這種狀況下,self對象將獲取一個新的關聯的對象anObject,且內存管理策略是自動retain關聯對象,當self對象釋放時,會自動release關聯對象。另外,若是咱們使用同一個key來關聯另一個對象時,也會自動釋放以前關聯的對象,這種狀況下,先前的關聯對象會被妥善地處理掉,而且新的對象會使用它的內存。指針

1
id anObject = objc_getAssociatedObject(self, &myKey);

咱們能夠使用objc_removeAssociatedObjects函數來移除一個關聯對象,或者使用objc_setAssociatedObject函數將key指定的關聯對象設置爲nil。code

咱們下面來用實例演示一下關聯對象的使用方法。

假定咱們想要動態地將一個Tap手勢操做鏈接到任何UIView中,而且根據須要指定點擊後的實際操做。這時候咱們就能夠將一個手勢對象及操做的block對象關聯到咱們的UIView對象中。這項任務分兩部分。首先,若是須要,咱們要建立一個手勢識別對象並將它及block作爲關聯對象。以下代碼所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)setTapActionWithBlock:(void (^)(void))block
{
     UITapGestureRecognizer *gesture = objc_getAssociatedObject(self, &kDTActionHandlerTapGestureKey);
 
     if (!gesture)
     {
         gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(__handleActionForTapGesture:)];
         [self addGestureRecognizer:gesture];
         objc_setAssociatedObject(self, &kDTActionHandlerTapGestureKey, gesture, OBJC_ASSOCIATION_RETAIN);
     }
 
     objc_setAssociatedObject(self, &kDTActionHandlerTapBlockKey, block, OBJC_ASSOCIATION_COPY);
}

這段代碼檢測了手勢識別的關聯對象。若是沒有,則建立並創建關聯關係。同時,將傳入的塊對象鏈接到指定的key上。注意block對象的關聯內存管理策略。

手勢識別對象須要一個target和action,因此接下來咱們定義處理方法:

1
2
3
4
5
6
7
8
9
10
11
12
- ( void )__handleActionForTapGesture:(UITapGestureRecognizer *)gesture
{
     if (gesture.state == UIGestureRecognizerStateRecognized)
     {
         void (^action)( void ) = objc_getAssociatedObject(self, &kDTActionHandlerTapBlockKey);
 
         if (action)
         {
             action();
         }
     }
}

咱們須要檢測手勢識別對象的狀態,由於咱們只須要在點擊手勢被識別出來時才執行操做。

從上面的例子咱們能夠看到,關聯對象使用起來並不複雜。它讓咱們能夠動態地加強類現有的功能。咱們能夠在實際編碼中靈活地運用這一特性。

相關文章
相關標籤/搜索