Objective-C開發(fā)者應該小心謹慎地遵循這個危險咒語的各種準則芽狗。一個很好的原因的就是:混亂的運行時代碼會改變運行在其架構之上的所有代碼檬寂。
當然,<objc/runtime.h>中的函數能實現(xiàn)許多強大的功能籍铁,而且又是其他很難做到的功能堡距。以我自己遇到的舉例:曾接手一個項目,繼承混亂媚送,要加新功能之碗,對每個頁面進行統(tǒng)計。能做到對現(xiàn)有代碼沒有侵入性的方法就是runtime季希,通過關聯(lián)系統(tǒng)方法褪那,自定義方法去實現(xiàn)真正的功能,僅僅需要一個UIViewController的category就可以做到式塌。
Associated Objects(關聯(lián)對象)
主要函數
在系統(tǒng)文件<objc/runtime.h>中很容易就找到主要相關的三個函數:
/**
* Sets an associated value for a given object using a given key and association policy.
*
* @param object The source object for the association.
* @param key The key for the association.
* @param value The value to associate with the key key for object. Pass nil to clear an existing association.
* @param policy The policy for the association. For possible values, see “Associative Object Behaviors.”
*
* @see objc_setAssociatedObject
* @see objc_removeAssociatedObjects
*/
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
/**
* Returns the value associated with a given object for a given key.
*
* @param object The source object for the association.
* @param key The key for the association.
*
* @return The value associated with the key \e key for \e object.
*
* @see objc_setAssociatedObject
*/
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
/**
* Removes all associations for a given object.
*
* @param object An object that maintains associated objects.
*
* @note The main purpose of this function is to make it easy to return an object
* to a "pristine state”. You should not use this function for general removal of
* associations from objects, since it also removes associations that other clients
* may have added to the object. Typically you should use \c objc_setAssociatedObject
* with a nil value to clear an association.
*
* @see objc_setAssociatedObject
* @see objc_getAssociatedObject
*/
OBJC_EXPORT void objc_removeAssociatedObjects(id object)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
函數的命名意思十分明顯
- objc_setAssociatedObject 給對象添加關聯(lián)對象
- objc_getAssociatedObject 獲取關聯(lián)對象
- objc_removeAssociatedObjects 移除所有關聯(lián)對象(返回一個對象為"原始狀態(tài)")
這里第三個函數注意下上面官方的注釋
The main purpose of this function is to make it easy to return an object to a "pristine state”. You should not use this function for general removal of associations from objects, since it also removes associations that other clients may have added to the object. Typically you should use \c objc_setAssociatedObject with a nil value to clear an association.
因為這個函數會移除所有的關聯(lián)對象博敬,很可能把別人添加的關聯(lián)對象也刪除。所以通常使用objc_setAssociatedObject函數傳入nil值根據key移除存在的某關聯(lián)對象峰尝。
函數參數
id object | 被關聯(lián)對象 |
---|---|
const void *key | 唯一的key值 |
id value | 關聯(lián)對象 |
objc_AssociationPolicy policy | 對關聯(lián)對象的持有策略 |
其中key的值是void指針偏窝,可以用三種方式做唯一值。
- 聲明static char SomeAssociatedKey; 使用&SomeAssociatedKey 作為唯一值武学。
- 聲明static void *SomeAssociatedKey; 使用SomeAssociatedKey 作為唯一值祭往。
- 利用selector函數地址作為唯一值。
屬性可以根據定義在枚舉類型objc_AssociationPolicy policy上的行為被關聯(lián)在對象上:
OBJC_ASSOCIATION_ASSIGN | @property (assign) 或 @property (unsafe_unretained) | 指定一個關聯(lián)對象的弱引用火窒。 |
---|---|---|
OBJC_ASSOCIATION_RETAIN_NONATOMIC | @property (nonatomic, strong) | 指定一個關聯(lián)對象的強引用硼补,不能被原子化使用。 |
OBJC_ASSOCIATION_COPY_NONATOMIC | @property (nonatomic, copy) | 指定一個關聯(lián)對象的copy引用熏矿,不能被原子化使用已骇。 |
OBJC_ASSOCIATION_RETAIN | @property (atomic, strong) | 指定一個關聯(lián)對象的強引用离钝,能被原子化使用。 |
OBJC_ASSOCIATION_COPY | @property (atomic, copy) | 指定一個關聯(lián)對象的copy引用褪储,能被原子化使用卵渴。 |
簡單例子
以UIButton為例為UIButton的category添加block屬性
#import <UIKit/UIKit.h>
typedef void(^touchBlock)(void);
@interface UIButton (Block)
- (void)handleTouchUpInside:(touchBlock)block;
@end
#import "UIButton+Block.h"
#import <objc/runtime.h>
@implementation UIButton (Block)
- (void)handleTouchUpInside:(touchBlock)block {
objc_setAssociatedObject(self, @selector(handleTouchUpInside:), block, OBJC_ASSOCIATION_COPY_NONATOMIC);
[self addTarget:self action:@selector(touchUpInsideBlock) forControlEvents:UIControlEventTouchUpInside];
}
- (void)touchUpInsideBlock {
touchBlock block = objc_getAssociatedObject(self, @selector(handleTouchUpInside:));
if (block) {
block();
}
}
@end
這樣為UIButton添加block屬性,在使用的時候代碼會比較集中:
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
button.backgroundColor = [UIColor redColor];
[button handleTouchUpInside:^{
NSLog(@"觸發(fā)touchUpInside事件");
}];
[self.view addSubview:button];
比起其他解決問題的方法鲤竹,關聯(lián)對象應該被視為最后的選擇(事實上category也不應該作為首選方法)浪读。
和其他精巧的trick、hack辛藻、workaround一樣碘橘,一般人都會在剛學習完之后樂于尋找場景去使用一下。盡你所能去理解和欣賞它在正確使用時它所發(fā)揮的作用揩尸,同時當你選擇這個解決辦法時蛹屿,也要避免當被輕蔑地問起“這是個什么玩意屁奏?”時的尷尬岩榆。
相關參考: Associated Objects