本文轉載請注明出處——polobymulberry-博客園
兩個函數名稱中都有associate,意思是關聯
,這里的關聯表示的是一種 從屬
關系,即有一個關聯者
和被關聯者
,我們說NSArray的對象array關聯了NSString對象string
,這里的array
就是關聯者
(表示主動關聯別人),string
就是被關聯者
(表示被動被別人關聯)。我們就會產生三個哲學問題。
- 關聯是什么?
- 為什么關聯?
- 怎么關聯?
既然關聯是要有關聯者
和被關聯者
,我們可以看到objc_setAssociatedObject
這個函數,就是為了設定關聯關系,說簡單點,就是為了把兩個對象關聯起來。
// objc_setAssociatedObject函數原型
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
- OBJC_EXPORT 打包lib時,用來說明該函數是暴露給外界調用的。
主要看該函數的幾個參數:
- id object 表示關聯者,是一個對象,變量名理所當然也是object
- id value 表示被關聯者,我們可以看到它的變量名是value,我們這里一定要理解
這個value最后是要關聯到object上
的。
關聯者和被關聯者有了,那么又會產生兩個問題,而答案正好對應函數的另外兩個參數。
- 一個就是我
日后如何獲取這個關聯關系
,也就是說,我今后要使用這個關聯關系,如何獲取?有人會說我直接使用根據object(關聯者)來獲取不就行了,但是一個object可能有許多被關聯者,比如上面的NSArray可以關聯一個NSString,也可以關聯一個NSDictionary。有人說那直接用關聯者和被關聯者兩個去獲取不就行了。但是使用一個關鍵詞key(一般是一個字符串),更合適。因為字符串可以寫成全局的,這樣可以在各個地方都自由獲取到關聯的對象了。而如果直接使用關聯的對象來索引,並且當這個關聯對象是局部變量時,那么在別的函數中要獲取的時候,就很麻煩了,此時你要不然就寫成全局的,那樣代碼將很亂,所以我覺得統一使用字符串來索引關聯關系更合適。我們看看apple是怎么設計獲取關聯的方法
:
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
你看,只需要一個object(關聯者)和一個key來獲取關聯。這里我主觀臆測此處一個關聯者的關聯關系使用hashtable
來表示的(我猜測的,還未深入研究,大神請拍磚
)。根據objc_getAssociatedObject
的參數,我們理所當然就會在set函數中使用一個key來表示關聯關系了。大概就像下面這樣:
- 另外,這個被關聯的對象也是要存在內存中的,那它的內存管理方式如何?
objc_setAssociatedObject
函數中還有一個參數是
objc_AssociationPolicy policy
它是一個枚舉,具體如下
enum {
OBJC_ASSOCIATION_ASSIGN = 0,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
OBJC_ASSOCIATION_RETAIN = 01401,
OBJC_ASSOCIATION_COPY = 01403
};
這個字面上看是指關聯策略,但是這里的策略更注重關聯關系的內存管理
方面。我們這里可以大膽意淫,這個被關聯者value其實很像object的一個屬性(property),當然,我們還可以擴展方法。看到這里,有人可能會說這不就是category嗎?可以說是,也可以說不是。是的原因是它們都擴展了OC對象的屬性,不是的原因是category是編譯時
就決定了擴展的屬性,而objc_setAssociatedObject是運行時
來擴展屬性。
比如我們使用
objc_setAssociatedObject(array, &key, string, OBJC_ASSOCIATION_RETAIN);
相當於string是array的一個retain屬性,嗯。。。你可以理解為strong。所以當array銷毀時,string也自然就不存在了。
上面看懂了,使用就很方便了。舉個最簡單的例子,把一個NSString對象關聯到NSArray對象上。
#import "ViewController.h"
// 使用objc_getAssociatedObject和objc_setAssociatedObject
// 需要添加objective-c的運行時文件
#import <objc/runtime.h>
// 表示關聯關系的key,主要目的是用來索引
const NSString *associatedKey = @"associate_nsarray_with_nsstring_key";
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *array = [NSArray arrayWithObjects:@"hello", @"world", @"!", nil];
NSString *string = @"I am an iOS developer!";
// 將string關聯到array上
objc_setAssociatedObject(array, &associatedKey, string, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// 從array中獲取被關聯的對象string
// 注意,這里就沒有string這個對象任何事了
// string其實已經變成了array的一個屬性值
NSString *getAssociatedObject = objc_getAssociatedObject(array, &associatedKey);
NSLog(@"%@", getAssociatedObject);
}
@end
輸出結果
當然,oc中關聯的方法可以做很多事,網上一搜一大把。我覺得首先明白這兩個函數是做什么的,至於怎么用,什么最佳實踐,還是去實踐中體會了。