標籤:
動態語言
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]; alert.delegate = self;}- (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 other2015-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 other2015-08-11 22:53:54.353 Dynamic[4655:2849502] addition2015-08-11 22:53:55.804 Dynamic[4655:2849502] click cancel2015-08-11 22:53:57.491 Dynamic[4655:2849502] click other2015-08-11 22:53:57.491 Dynamic[4655:2849502] (null)
首先我們使用了關聯對象,所以點擊other的時候會看到列印出了addition。點擊cancel的時候又因為我們remove了關聯對象,此時再點擊other的時候addition就變成null了。
Objective-C——關聯對象