關聯對象的簡單使用:web
#import "wangmumu.h"
@interface
wangmumu ()
{
}
static
char
staticString;
@implementation
SHVCarsViewController
- (
void
)viewDidLoad {
[
super
viewDidLoad];
// Do any additional setup after loading the view.
self
.edgesForExtendedLayout=
UIRectEdgeNone
;
self
.tableView.hidden =
YES
;
UIButton
*btn = [[
UIButton
alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
btn.backgroundColor = [
UIColor
grayColor];
[btn addTarget:
self
action:
@selector
(btnn:) forControlEvents:
UIControlEventTouchUpInside
];
objc_setAssociatedObject(btn, &staticString, @
"aaaa"
, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[
self
.view addSubview:btn];
}
- (
void
)btnn:(
UIButton
*)btn{
NSString
*str = objc_getAssociatedObject(btn, &staticString);
NSLog
(@
"%@"
,str);
}
/*
objc_setAssociatedObject(btn, &staticString, @"aaaa", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
有四個參數
1.被關聯的對象
2.要關聯的對象的鍵值,通常設置成靜態的,用於獲取關聯對象的值
3.要傳得值 通常是靜態變量
4.關聯時採用的協議,有assign,retain,copy等協議 點擊進去選取枚舉
*/
關聯對象(Associated Object)
關聯對象是Runtime中一個很是實用的特性,不過可能很容易被忽視。多線程
關聯對象相似於成員變量,不過是在運行時添加的。咱們一般會把成員變量(Ivar)放在類聲明的頭文件中,或者放在類實現的@implementation後面。但這有一個缺點,咱們不能在分類中添加成員變量。若是咱們嘗試在分類中添加新的成員變量,編譯器會報錯。ide
咱們可能但願經過使用(甚至是濫用)全局變量來解決這個問題。但這些都不是Ivar,由於他們不會鏈接到一個單獨的實例。所以,這種方法不多使用。函數
Objective-C針對這一問題,提供了一個解決方案:即關聯對象(Associated Object)。編碼
咱們能夠把關聯對象想象成一個Objective-C對象(如字典),這個對象經過給定的key鏈接到類的一個實例上。不過因爲使用的是C接口,因此key是一個void指針(const void *)。咱們還須要指定一個內存管理策略,以告訴Runtime如何管理這個對象的內存。這個內存管理的策略能夠由如下值指定:spa
1
2
3
4
5
OBJC_ASSOCIATION_ASSIGN
OBJC_ASSOCIATION_RETAIN_NONATOMIC
OBJC_ASSOCIATION_COPY_NONATOMIC
OBJC_ASSOCIATION_RETAIN
OBJC_ASSOCIATION_COPY
當宿主對象被釋放時,會根據指定的內存管理策略來處理關聯對象。若是指定的策略是assign,則宿主釋放時,關聯對象不會被釋放;而若是指定的是retain或者是copy,則宿主釋放時,關聯對象會被釋放。咱們甚至能夠選擇是不是自動retain/copy。當咱們須要在多個線程中處理訪問關聯對象的多線程代碼時,這就很是有用了。.net
咱們將一個對象鏈接到其它對象所須要作的就是下面兩行代碼:線程
1
2
3
static char myKey;
objc_setAssociatedObject(self, &myKey, anObject, OBJC_ASSOCIATION_RETAIN);
在這種狀況下,self對象將獲取一個新的關聯的對象anObject,且內存管理策略是自動retain關聯對象,當self對象釋放時,會自動release關聯對象。另外,若是咱們使用同一個key來關聯另一個對象時,也會自動釋放以前關聯的對象,這種狀況下,先前的關聯對象會被妥善地處理掉,而且新的對象會使用它的內存。指針
1
id anObject = objc_getAssociatedObject(self, &myKey);
咱們能夠使用objc_removeAssociatedObjects函數來移除一個關聯對象,或者使用objc_setAssociatedObject函數將key指定的關聯對象設置爲nil。code
咱們下面來用實例演示一下關聯對象的使用方法。
假定咱們想要動態地將一個Tap手勢操做鏈接到任何UIView中,而且根據須要指定點擊後的實際操做。這時候咱們就能夠將一個手勢對象及操做的block對象關聯到咱們的UIView對象中。這項任務分兩部分。首先,若是須要,咱們要建立一個手勢識別對象並將它及block作爲關聯對象。以下代碼所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)setTapActionWithBlock:(void (^)(void))block
{
UITapGestureRecognizer *gesture = objc_getAssociatedObject(self, &kDTActionHandlerTapGestureKey);
if (!gesture)
{
gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(__handleActionForTapGesture:)];
[self addGestureRecognizer:gesture];
objc_setAssociatedObject(self, &kDTActionHandlerTapGestureKey, gesture, OBJC_ASSOCIATION_RETAIN);
}
objc_setAssociatedObject(self, &kDTActionHandlerTapBlockKey, block, OBJC_ASSOCIATION_COPY);
}
這段代碼檢測了手勢識別的關聯對象。若是沒有,則建立並創建關聯關係。同時,將傳入的塊對象鏈接到指定的key上。注意block對象的關聯內存管理策略。
手勢識別對象須要一個target和action,因此接下來咱們定義處理方法:
1
2
3
4
5
6
7
8
9
10
11
12
- (
void
)__handleActionForTapGesture:(UITapGestureRecognizer *)gesture
{
if
(gesture.state == UIGestureRecognizerStateRecognized)
{
void
(^action)(
void
) = objc_getAssociatedObject(self, &kDTActionHandlerTapBlockKey);
if
(action)
{
action();
}
}
}
咱們須要檢測手勢識別對象的狀態,由於咱們只須要在點擊手勢被識別出來時才執行操做。
從上面的例子咱們能夠看到,關聯對象使用起來並不複雜。它讓咱們能夠動態地加強類現有的功能。咱們能夠在實際編碼中靈活地運用這一特性。