有時(shí)候需要在對(duì)象中存放相關(guān)信息佳遂,這時(shí)我們一般會(huì)從對(duì)象所屬的類中繼承一個(gè)子類,然后改用這個(gè)子類對(duì)象路捧,然而并非所有情況下都能這么做关霸,有時(shí)候類的實(shí)例可能是有魔種機(jī)制所創(chuàng)建的,而開(kāi)發(fā)者無(wú)法令這種機(jī)制創(chuàng)建出自己所寫(xiě)的子類實(shí)例杰扫。OC中有一項(xiàng)強(qiáng)大的特性可以解決此問(wèn)題队寇,這就是“關(guān)聯(lián)對(duì)象”。
可以給某個(gè)對(duì)象關(guān)聯(lián)許多其他對(duì)象涉波,這些對(duì)象通過(guò)“鍵”來(lái)區(qū)分。存儲(chǔ)對(duì)象值的時(shí)候炭序,可以指明“存儲(chǔ)策略”啤覆,用以維護(hù)相應(yīng)的“內(nèi)存管理語(yǔ)義”。
OBJC_ASSOCIATION_ASSIGN --- assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC --- nonatomic, retain
OBJC_ASSOCIATION_COPY_NONATOMIC --- nonatomic, copy
OBJC_ASSOCIATION_RETAIN --- retain
OBJC_ASSOCIATION_COPY --- copy
下列方法可以管理關(guān)聯(lián)對(duì)象:
- void objc_setAssociatedObject (id object, void *key, id value, objc_AssociationPolicy policy) // 此方法以給定的鍵和策略為某對(duì)象設(shè)置關(guān)聯(lián)對(duì)象值
- id objc_getAssociatedObject(id object, void *key) // 此方法根據(jù)給定的鍵從某個(gè)對(duì)象中獲取相應(yīng)的關(guān)聯(lián)對(duì)象值
- void objc_removeAssociatedObject(id object) // 此方法移除指定對(duì)象的全部關(guān)聯(lián)對(duì)象
我們可以把某對(duì)象想象成NSDictionary, 把關(guān)聯(lián)到該對(duì)象的值理解為字典中的條目惭聂,于是窗声,存取關(guān)聯(lián)對(duì)象的值就相當(dāng)于在NSDictionary對(duì)象上調(diào)用[objct setObject:value forKey:key]與[object objectForKey:key]方法,但是兩者之間還是有重要的差別的:設(shè)置關(guān)聯(lián)對(duì)象時(shí)用的鍵是個(gè)“不透明指針”(其所指向的數(shù)據(jù)結(jié)構(gòu)不局限于某中特定的數(shù)據(jù)類型的指針)辜纲。如果在兩個(gè)鍵上調(diào)用“isEqual:”方法的返回值是YES笨觅,那么NSDictionary就認(rèn)為兩者相等,不然在設(shè)置關(guān)聯(lián)對(duì)象值時(shí)耕腾,若想令兩個(gè)健匹配到相同的一個(gè)值见剩,則二者必須是完全相同的指針才行。所以扫俺,在設(shè)置關(guān)聯(lián)對(duì)象值時(shí):通常使用靜態(tài)全局變量做鍵苍苞。
關(guān)聯(lián)對(duì)象的舉例
比方說(shuō):我們?cè)谑褂肬IAlertView時(shí),一般這樣寫(xiě):
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Question" message:@"what do you want to do" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"繼續(xù)", nil];
[alert show];
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
[self doCancel];
} else {
[self doContinue];
}
}
如果在同一個(gè)類中處理多個(gè)警告信息視圖,那么代碼會(huì)變的很復(fù)雜羹呵。這個(gè)可以使用關(guān)聯(lián)對(duì)象來(lái)做骂际,創(chuàng)建完一個(gè)視圖之后,設(shè)定一個(gè)與之關(guān)聯(lián)的“塊(Block)”,等到執(zhí)行delegate方法時(shí)再將其讀出來(lái)冈欢。
#import <objc/runtime.h>
static void *CWGMyAlertViewKey = @"CWGMyAlertViewKey";
- (void)askUserQuestion {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Question" message:@"what do you want to do" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"繼續(xù)", nil];
void (^block)(NSInteger) = ^(NSInteger buttonIndex) {
if (buttonIndex == 0) {
[self doCancel];
} else {
[self doContinue];
}
};
objc_setAssociatedObject(alter, CWGMyAlertViewKey, block, OBJC_ASSOCIATION_COPY);
[alter show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
void (^block)(NSInteger) = objc_getAssociatedObject(alertView, CWGMyAlertViewKey);
block(buttonIndex);
}
這樣寫(xiě)之后歉铝,有沒(méi)有發(fā)現(xiàn)代碼更加易讀了?但是需要注意的是:塊可能捕獲某些變量凑耻,這樣會(huì)造成“保留環(huán)”太示!這種做大雖然很有用,但是這只是在其他辦法行不通的時(shí)候才去考慮它拳话,若是濫用先匪,則很快就會(huì)使代碼失控,使其難于調(diào)試弃衍。其實(shí)創(chuàng)建這樣的UIAlertView還有一個(gè)辦法呀非,那就是從中繼承子類,把塊保存為子類中的屬性镜盯,筆者認(rèn)為:若是需要多次用到Alter視圖岸裙,這樣的做法比關(guān)聯(lián)對(duì)象要好很多。
總結(jié):
- 可以通過(guò)關(guān)聯(lián)對(duì)象機(jī)制把兩個(gè)對(duì)象連起來(lái)速缆。
- 定義關(guān)聯(lián)對(duì)象時(shí)可以定義內(nèi)存管理語(yǔ)義降允,用以模仿定義屬性時(shí)所用的“擁有關(guān)系”與“非擁有關(guān)系”。
- 只有在其他做法不可行的情況下艺糜,才去選用關(guān)聯(lián)對(duì)象剧董, 因?yàn)檫@種做法通常會(huì)引入難于查找的bug。