5.關聯屬性(associative)
?????? category與associative作為OC的擴展機制的兩個特性,category即類別,可以通過它來擴展方法;associative,可以通過它來擴展屬性(實現增加屬性的功能,而非真正的增加了屬性,可以通過運行時打印屬性列表驗證);在開發中,可能category比較常見,相對的associative,就用的比較少,要用它必須使用的頭文件,然后就可以自由使用objc_getAssociatedObject以及objc_setAssociatedObject。
在類的定義之外為類增加額外的存儲空間:
??????? 使用關聯,我們可以不用修改類的定義而為其對象增加存儲空間。這在我們無法訪問到類的源碼的時候或者是考慮到二進制兼容性的時候是非常有用。
關聯是基于關鍵字的,因此,我們可以為任何對象增加任意多的關聯,每個都使用不同的關鍵字即可。關聯是可以保證被關聯的對象在關聯對象的整個生命周期都是可用的(在垃圾自動回收環境下也不會導致資源不可回收)。
1).創建關聯
創建關聯要使用到objective-c的運行時函數:objc_setAssociatedObject來把一個對象與另外一個對象進行關聯。該函數需要四個參數:源對象,關鍵字,關聯的對象和一個關聯策略。當然,此處的關鍵字和關聯策略是需要進一步討論的。
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
參數說明:
(1). 關聯源(給誰關聯);
(2).關聯關鍵字 (用于存取值,相當于字典的鍵值), 關鍵字是一個void類型的指針。每一個關聯的關鍵字必須是唯一的。通常都是會采用靜態變量來作為關鍵字;
(3).關聯關鍵字對應的值 (相當于字典的鍵對應的值);
(4).關聯策略,與聲明屬性時的copy,strong等相對應;
2).獲取相關聯的對象
id objc_getAssociatedObject(id object, const void *key)
3).斷開關聯
斷開關聯是使用objc_setAssociatedObject函數,傳入nil值即可。
4).移除所有關聯
void objc_removeAssociatedObjects(id object)
關聯屬性使用案例:
案例一:給系統類增加屬性
#import@interface UIButton (property)
@property (nonatomic, copy)NSString *name;
@end
#import "UIButton+property.h"
#import <objc/runtime.h>
static char *nameKey;
@implementation UIButton (property)
- (void)setName:(NSString *)name{
/*
* 關聯源(給誰關聯)
* 關聯關鍵字 (用于存取值,相當于字典的鍵值)
* 關聯關鍵字對應的值 (相當于字典的鍵對應的值)
* 關聯策略,與聲明屬性時的copy,strong等相對應
*/
???????? objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY);
}
- (NSString *)name{
????????? return objc_getAssociatedObject(self, &nameKey);
}
@end
#import "ViewController.h"#import "Person.h"#import//消息發送機制底層框架#import#import "UIButton+property.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIButton *b = [[UIButton alloc]init];
b.name = @"我的名字叫按鈕";
NSLog(@"---%@---",b.name);
}
輸出日志:
2017-08-14 17:23:50.641 runtime-消息發送[5124:259794] ---我的名字叫按鈕---
案例二:充當全局的字典
????? 使用場景:在UITableView中點擊某一個cell,這時候彈出一個UIAlertView,然后在UIAlertView消失的時候獲取此cell的信息,我們就獲取cell的indexPath
#import
static char kUITableViewIndexKey;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
......
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示"
message:@"這里是xx樓"
delegate:self
cancelButtonTitle:@"好的"
otherButtonTitles:nil];
//然后這里設定關聯,此處把indexPath關聯到alert上
objc_setAssociatedObject(alert, &kUITableViewIndexKey, indexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[alert show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) {
NSIndexPath *indexPath = objc_getAssociatedObject(alertView, &kUITableViewIndexKey);
NSLog(@"%@", indexPath);
}
實際上這種設置和獲取的方法,就相當于一個全局的字典,用來存取鍵值對。