【原】objc_setAssociatedObject和objc_getAssociatedObject


本文轉載請注明出處——polobymulberry-博客園
兩個函數名稱中都有associate,意思是關聯,這里的關聯表示的是一種 從屬關系,即有一個關聯者被關聯者,我們說NSArray的對象array關聯了NSString對象string,這里的array就是關聯者(表示主動關聯別人),string就是被關聯者(表示被動被別人關聯)。我們就會產生三個哲學問題。

  1. 關聯是什么?
  2. 為什么關聯?
  3. 怎么關聯?

既然關聯是要有關聯者被關聯者,我們可以看到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中關聯的方法可以做很多事,網上一搜一大把。我覺得首先明白這兩個函數是做什么的,至於怎么用,什么最佳實踐,還是去實踐中體會了。


免責聲明!

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



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