分類機(jī)制通常用于向無(wú)源碼的既有類中新增功能萌抵。這個(gè)特性極為強(qiáng)大,但在使用時(shí)也很容易忽視其中可能產(chǎn)生的問(wèn)題巫湘。這個(gè)問(wèn)題在于: 分類中的方法是直接添加在類里面的娄猫,它們就好比這個(gè)類中的固有方法。將分類方法加入類中這一操作是在運(yùn)行期系統(tǒng)加載分類時(shí)完成的外冀。運(yùn)行期系統(tǒng)會(huì)把分類中的每個(gè)方法都加入類的方法列表中寡键。如果類中本來(lái)就有此方法,而分類又實(shí)現(xiàn)了一次雪隧,那么分類中的方法都加入類的方法列表中西轩。如果類中本來(lái)就有此方法,而分類又實(shí)現(xiàn)了一次脑沿,那么分類中的方法會(huì)覆蓋原來(lái)那一份實(shí)現(xiàn)代碼藕畔。實(shí)際上可能會(huì)發(fā)生很多次覆蓋,比如某個(gè)分類中的方法覆蓋了"主實(shí)現(xiàn)"中的相關(guān)方法庄拇,而另外一個(gè)分類中的方法又覆蓋了這個(gè)分類中的方法注服。多次覆蓋的結(jié)果以最后一個(gè)分類為準(zhǔn)。
比方說(shuō)措近,要給NSString添加分類溶弟,并在其中提供一些輔助方法,用于處理與HTTP URL有關(guān)的字符串熄诡。你可能會(huì)把分類寫成這樣:
@interface NSString (HTTP)
// Encode a string with URL encoding
- (NSString*)urlEncodedString;
// Decode a URL encoded string
- (NSString*)urlDecodedString;
@end
現(xiàn)在看起來(lái)沒(méi)什么問(wèn)題可很,可是诗力,如果還有一個(gè)分類也往NSString里添加方法凰浮,那會(huì)如何呢我抠?那個(gè)分類里可能也有個(gè)名叫urlEncodedString的方法,其代碼與你所添加的大同小異袜茧,但卻不能正確實(shí)現(xiàn)你所需的功能菜拓。那個(gè)分類的加載時(shí)機(jī)如果晚于你所寫的這個(gè)分類,那么其代碼就會(huì)把你的那一份覆蓋掉笛厦,這樣的話纳鼎,你在代碼中調(diào)用urlEncodedString方法時(shí),實(shí)際執(zhí)行的是那個(gè)分類里的實(shí)現(xiàn)代碼裳凸。由于其執(zhí)行結(jié)果和你預(yù)期的值不同贱鄙,所以自己所寫的那些代碼也許就無(wú)法正常運(yùn)行了。這種bug很難追查姨谷,因?yàn)槟憧赡芤庾R(shí)不到實(shí)際執(zhí)行的urlEncodedString代碼并不是自己實(shí)現(xiàn)的那一份逗宁。
要解決此問(wèn)題,一般的做法是: 以命名空間來(lái)區(qū)別各個(gè)分類的名稱與其中所定義的方法梦湘。想在Objective-C中實(shí)現(xiàn)命名空間功能瞎颗,只有一個(gè)辦法,就是給相關(guān)名稱都加上某個(gè)共用的前綴捌议。與給類名加前綴(參見(jiàn)第15條)時(shí)所應(yīng)考慮的因素相似哼拔,給分類所加的前綴也要選得恰當(dāng)才行。一般來(lái)說(shuō)瓣颅,這個(gè)前綴應(yīng)該與應(yīng)用程序或程序中其他地方所用的前綴相同倦逐。于是,我們可以給剛才那個(gè)NSString分類加上ABC前綴:
@interface NSString (ABC_HTTP)
// Encode a string with URL encoding
- (NSString*)abc_urlEncodedString;
// Decode a URL encoded string
- (NSString*)abc_urlDecodedString;
@end
從技術(shù)角度講宫补,并不是非得命名空間把各個(gè)分類的名稱區(qū)隔開(kāi)不可僻孝。即便兩個(gè)分類重名了,也不會(huì)出錯(cuò)守谓。然而這樣做不好穿铆,編譯器會(huì)發(fā)出類似下面這種警告信息:
warning: duplicate definition of category 'HTTP' on interface 'NSString'
即便加了前綴,也難保其他分類不會(huì)覆蓋你所寫的方法斋荞,然而幾率卻小了很多荞雏,因?yàn)槠渌绦驇?kù)很少會(huì)和你選用同一個(gè)前綴。這樣做也能避免類的開(kāi)發(fā)者以后在更新該類時(shí)所添加的方法與你在分類中添加方法重名平酿。比方說(shuō)凤优,假如蘋果公司決定在NSString類里添加urlEncodedString方法,而你在分類中所寫的方法又沒(méi)加前綴蜈彼,那么可能就會(huì)覆蓋蘋果公司的方法筑辨,這樣做不合適,因?yàn)镹SString類的其他使用者想得到由蘋果公司實(shí)現(xiàn)的代碼所輸出的結(jié)果幸逆,而非你所返回的那個(gè)結(jié)果棍辕。還有一種可能暮现,就是蘋果公司編寫的實(shí)現(xiàn)代碼帶有一些附加效果,該方法若為你所寫的代碼所覆蓋楚昭,則會(huì)令對(duì)象內(nèi)的數(shù)據(jù)互不一致栖袋,從而造成難于查找的bug。
此外還要記住抚太,如果向某個(gè)類的分類中加入方法塘幅,那么在應(yīng)用程序中,該類的每個(gè)實(shí)例均可調(diào)用這些方法尿贫。比方說(shuō)电媳,若是向NSString、NSArray庆亡、NSNumber這種系統(tǒng)類里添加方法匆背,那么這些類的每個(gè)實(shí)例均可調(diào)用你所加的方法,即便這些實(shí)例不是由你的代碼創(chuàng)建出來(lái)的身冀,也依然會(huì)如此钝尸。如果你無(wú)意中把自己分類里的方法名起得和其他分類一樣,或是與第三方庫(kù)所添分類中的方法重名了搂根,那么就可能出現(xiàn)奇怪的bug珍促,因?yàn)槟阋詾榇朔椒▓?zhí)行的是自己所寫的那份代碼,然而實(shí)際上卻不是剩愧。與之相似猪叙,刻意覆寫分類中的方法也不好,尤其是當(dāng)你把代碼發(fā)布為程序庫(kù)供其他開(kāi)發(fā)者使用仁卷,而他們又要依賴系統(tǒng)中現(xiàn)存的功能時(shí)穴翩,更不應(yīng)該這么做。若是其他開(kāi)發(fā)者又覆寫了同一個(gè)方法锦积,那么情況會(huì)更糟芒帕,因?yàn)闊o(wú)法確定最后到底會(huì)執(zhí)行哪份實(shí)現(xiàn)代碼。這又一次說(shuō)明了為何要給分類中的方法名加上前綴丰介。
要點(diǎn)
- 向第三方類中添加分類時(shí)背蟆,總應(yīng)給其名稱加上你專用的前綴
- 向第三方類中添加分類時(shí),總應(yīng)給其中的方法名加上你專用的前綴