Associative機制使用場景

1.    概念

objective-c有兩個擴展機制:categoryassociative。我們可以通過category來擴展方法,但是它有個很大的侷限性,不能擴展屬性。於是,就有了專門用來擴展屬性的機制:associative

 

2.    使用方法

iOS開發過程中,category比較常見,而associative就用的比較少。associative的主要原理,就是把兩個對象相互關聯起來,使得其中的一個對象作爲另外一個對象的一部分。

使用associative,我們可以不用修改類的定義而爲其對象增加存儲空間。這在我們無法訪問到類的源碼的時候或者是考慮到二進制兼容性的時候是非常有用。

associative是基於關鍵字的。因此,我們可以爲任何對象增加任意多的associative,每個都使用不同的關鍵字即可。associative是可以保證被關聯的對象在關聯對象的整個生命週期都是可用的。

 

associative機制提供了三個方法:

OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, idvalue, objc_AssociationPolicy policy)

 

OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)

 

OBJC_EXPORT void objc_removeAssociatedObjects(id object)

 

 

2.1.創建associative

創建associative使用的是:objc_setAssociatedObject。它把一個對象與另外一個對象進行關聯。該函數需要四個參數:源對象,關鍵字,關聯的對象、關聯策略。

關鍵字是一個void類型的指針。每一個關聯的關鍵字必須是唯一的。通常都是會採用靜態變量來作爲關鍵字。

關聯策略表明了相關的對象是通過賦值,保留引用還是複製的方式進行關聯的;還有這種關聯是原子的還是非原子的。這裏的關聯策略和聲明屬性時的很類似。這種關聯策略是通過使用預先定義好的常量來表示的。

 

比如,我們想對一個UIView,添加一個NSString類型的tag。可以這麼做:

- (void)setTagString:(NSString *)value {

         objc_setAssociatedObject(self, KEY_TAGSTRING, value,OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

2.2.獲取associative對象

獲取相關聯的是函數objc_getAssociatedObject

繼續上面的例子,從一個UIView的實例中,獲取一個NSString類型的tag

- (NSString *)tagString {

         NSObject *obj = objc_getAssociatedObject(self, KEY_TAGSTRING);

         if (obj && [obj isKindOfClass:[NSString class]]) {

              return (NSString *)obj;

    }

        

         return nil;

}

 

2.3.斷開associative

斷開associative是使用objc_setAssociatedObject函數,傳入nil值即可。

objc_setAssociatedObject(self, KEY_TAGSTRING, nil,OBJC_ASSOCIATION_RETAIN_NONATOMIC);

 

使用函數objc_removeAssociatedObjects可以斷開所有associative。通常情況下不建議這麼做,因爲他會斷開所有關聯。

 

3.   應用場景

3.1.TagString

上面的例子提到,在UIView中添加NSString類型的標記,就是一個非常實用的例子。全部的代碼如下:

 

@interface UIView(BDTag)

 

@property (nonatomic, retain) NSString *tagString;

 

- (UIView *)viewWithTagString:(NSString *)value;

 

@end

 

#import "UIView+BDTag.h"

 

#undef   KEY_TAGSTRING

#define KEY_TAGSTRING     "UIView.tagString"

 

@implementation UIView(BDTag)

 

@dynamic tagString;

 

- (NSString *)tagString {

         NSObject *obj = objc_getAssociatedObject(self, KEY_TAGSTRING);

         if (obj && [obj isKindOfClass:[NSString class]]) {

              return (NSString *)obj;

    }

        

         return nil;

}

 

- (void)setTagString:(NSString *)value {

         objc_setAssociatedObject(self, KEY_TAGSTRING, value,OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

 

- (UIView *)viewWithTagString:(NSString *)value {

         if (nil == value) {

              return nil;

    }

        

         for (UIView *subview in self.subviews) {

              NSString *tag = subview.tagString;

              if ([tag isEqualToString:value])

              {

                     return subview;

              }

         }

        

         return nil;

}

 

@end

蘋果雖然有提供NSInteger類型的tag屬性,用於標記相應的ui。但是在處理比較複雜的邏輯的時候,往往NSInteger類型的標記不能滿足需求。爲其添加了NSString類型的標記後。就能使用字符串,快速的標記ui,並且使用viewWithTagString方法,快速找到你所需要的ui

 

3.2.NSObject子類添加任何信息

這是一個方便,強大,並且簡單的類。利用associative機制,爲任何Object,添加你所需要的信息。比如用戶登錄,向服務端發送用戶名/密碼時,可以將這些信息綁定在請求的項之中。等請求完成後,再取出你所需要的信息,進行邏輯處理。而不需要另外設置成員,保存這些數據。

具體的實現如下:

@interface NSObject (BDAssociation)

 

- (id)associatedObjectForKey:(NSString*)key;

- (void)setAssociatedObject:(id)object forKey:(NSString*)key;

 

@end

 

#import

 

#import "NSObject+BDAssociation.h"

 

@implementation NSObject (BDAssociation)

 

static char associatedObjectsKey;

 

- (id)associatedObjectForKey:(NSString*)key {

    NSMutableDictionary *dict = objc_getAssociatedObject(self, &associatedObjectsKey);

    return [dict objectForKey:key];

}

 

- (void)setAssociatedObject:(id)object forKey:(NSString*)key {

    NSMutableDictionary *dict = objc_getAssociatedObject(self, &associatedObjectsKey);

    if (!dict) {

        dict = [[NSMutableDictionary alloc] init];

        objc_setAssociatedObject(self, &associatedObjectsKey, dict,OBJC_ASSOCIATION_RETAIN);

    }

    [dict setObject:object forKey:key];

}

 

@end

 

3.3.內存回收檢測

記得在我剛開始學iOS開發的時候,經常出現內存泄露的問題,於是就在各個view controllerdealloc中打Log。這種方法雖然有效,但比較挫,不好管理。

這裏貼出一種漂亮的解決方案,利用associative機制。讓object在回收時,自動輸出回收信息。

@interface NSObject (BDLogDealloc)

 

- (void)logOnDealloc;

 

@end



#import "NSObject+BDLogDealloc.h"


static char __logDeallocAssociatedKey__;

 

@interface LogDealloc : NSObject

 

@property (nonatomic, copy) NSString* message;

 

@end

 

@implementation NSObject (LogDealloc)

 

- (void)logOnDealloc {

    if(objc_getAssociatedObject(self, &__logDeallocAssociatedKey__) == nil) {

        LogDealloc* log = [[[LogDealloc alloc] init] autorelease];

        log.message = NSStringFromClass(self.class);

        objc_setAssociatedObject(self, &__logDeallocAssociatedKey__, log,OBJC_ASSOCIATION_RETAIN);

    }

}

 

@end

 

@implementation LogDealloc

 

- (void)dealloc {

    NSLog(@"dealloc: %@", self.message);

    [_message release];

    [super dealloc];

}

 

@end

 

 

4.   總結

以上便是幾種associative機制的使用例子。這只是強大的associative功能中,小小的幾個縮影。有了associative,就能用簡單的幾行代碼,解決曾經困擾我們許久的問題。


本文轉載自:http://blog.sina.com.cn/s/blog_60342e330101tcz1.html


發佈了61 篇原創文章 · 獲贊 5 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章