Objective-C——關聯對象


動態語言

OC是一種動態語言,它的方法,對象的類型都是到運行的時候才能夠確定的。所以這就使得OC存在了關聯對象這一強大的機制。

 

關聯對象

所謂關聯對象,其實就是我們在運行時對一個已存在的對象上面綁定一個對象,使兩個對象變成動態的聚合關系。

關聯對象和屬性一樣有着關鍵字,以下是關聯對象的存儲策略:

關聯類型 等效的@property屬性
OBJC_ASSOCIATION_ASSIGN assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC nonatomic,retain
OBJC_ASSOCIATION_COPY_NONATOMIC nonatomic,copy
OBJC_ASSOCIATION_RETAIN retain
OBJC_ASSOCIATION_COPY copy

 

 

 

 

 

 

關聯對象主要靠下面三個函數,它們都位於<objc/runtime>下

void objc_setAssociatedObject(id object, void *key ,id value ,objc_AssociationPolicy policy)

設置關聯對象

參數 說明
object 要進行關聯的對象
key 一個內存表示,在比較兩個關聯對象是否相等時,比較的就是內存地址,所以一般用一個全局靜態變量表示
value 被關聯的對象
policy 存儲策略

 

 

 

 

 

id objc_getAssociatedObject(id object, void *key)

獲取關聯對象

void objc_removeAssociatedObjects(id object)

刪除關聯對象

 

作用

關聯對象一般用於動態的擴展一個對象,但是這一般都是在其他方法不行的事后才會使用,因為關聯對象很可能會出現難以查找的bug。

關聯對象有時也會用於在category向類添加屬性,這點等會兒在分析。

下面Demo在UIAlertView上面動態綁定了一個block,把按鈕處理邏輯和alert調用整合在了一起。

static char *AlertKey = "alertKey";

- (void)viewDidLoad {
    [super viewDidLoad];
   
    alert = [[UIAlertView alloc] initWithTitle:@"alert" message:@"message" delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"other", nil];
}

- (IBAction)click:(id)sender {
    
    void (^block) (NSInteger) = ^(NSInteger buttonIndex){
        if (buttonIndex == 0){
            NSLog(@"click cancel");
        }
        else{
            NSLog(@"click other");
        }
    };
    objc_setAssociatedObject(alert, AlertKey, block, OBJC_ASSOCIATION_COPY);
    [alert show];
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    void (^block)(NSInteger) = objc_getAssociatedObject(alertView, AlertKey);
    block(buttonIndex);
}

運行程序點擊兩個按鈕分別輸出如下

2015-08-11 22:51:27.146 Dynamic[4592:2833778] click other
2015-08-11 22:51:28.262 Dynamic[4592:2833778] click cancel

 

接下來我們給UIViewController在category中添加一個addition屬性

#import "ViewController.h"
#import <objc/runtime.h>

@interface UIViewController (Addition)

@property(nonatomic ,copy) NSString *addition;

@end
#import "UIViewController+Addition.h"

static const void *IndieBandNameKey = &IndieBandNameKey;

@implementation UIViewController (Addition)

-(void)setAddition:(NSString *)addition{
    objc_setAssociatedObject(self, IndieBandNameKey, addition, OBJC_ASSOCIATION_COPY);
}

-(NSString *)addition{
    return objc_getAssociatedObject(self, IndieBandNameKey);
}

@end

這里說明一下,這里關聯的實際上是形參addition,和屬性沒有什么關系,寫@property 就是為了能夠使用‘.’語法。但是@porperty里面的屬性存儲策略還是要和關聯對象一致的,這樣不容易造成誤解。

所以每次setAddition實際上我們並不是修改了原有內存的值,而是改變了指針指向的地址,這里需要注意一下。

然后修改剛才alert的代碼

- (void)viewDidLoad {
    [super viewDidLoad];
   
    alert = [[UIAlertView alloc] initWithTitle:@"alert" message:@"message" delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"other", nil];
    alert.delegate = self;
    self.addition = @"addition";
    
}

- (IBAction)click:(id)sender {
    
    void (^block) (NSInteger) = ^(NSInteger buttonIndex){
        if (buttonIndex == 0){
            NSLog(@"click cancel");
            objc_removeAssociatedObjects(self);
        }
        else{
            NSLog(@"click other");
            NSLog(@"%@",self.addition);
        }
    };
    objc_setAssociatedObject(alert, AlertKey, block, OBJC_ASSOCIATION_COPY);
    [alert show];
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    void (^block)(NSInteger) = objc_getAssociatedObject(alertView, AlertKey);
    block(buttonIndex);
}

注意三條加粗的語句,然后我們運行看結果

2015-08-11 22:53:54.353 Dynamic[4655:2849502] click other
2015-08-11 22:53:54.353 Dynamic[4655:2849502] addition
2015-08-11 22:53:55.804 Dynamic[4655:2849502] click cancel
2015-08-11 22:53:57.491 Dynamic[4655:2849502] click other
2015-08-11 22:53:57.491 Dynamic[4655:2849502] (null)

首先我們使用了關聯對象,所以點擊other的時候會看到打印出了addition。點擊cancel的時候又因為我們remove了關聯對象,此時再點擊other的時候addition就變成null了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM