第十三條:用“方法調(diào)配技術(shù)“調(diào)試”黑盒方法“
在消息解析時,與給定的選擇子名稱相對應(yīng)的方法是不是也可以在運(yùn)行時改變呢?
沒錯,就是這樣。
若能善用此特性,則可發(fā)揮出巨大優(yōu)勢,因為我們既不需要源代碼,也不需要通過繼承子類來覆寫方法就能改變這個類本身的功能。
這樣一來,新功能將在本類的所有實例中生效,而不是僅限于覆寫了相關(guān)方法的那些子類實例。
此方案經(jīng)常成為“方法調(diào)配”(method swizzling)。
類的方法列表會把選擇子的名稱映射到相關(guān)的方法實現(xiàn)之上,使得“動態(tài)消息派發(fā)系統(tǒng)”能夠據(jù)此找到應(yīng)該調(diào)用的方法。
這些方法均以函數(shù)指針的形式來表示,這種指針叫做IMP,其原型如下:
id (* IMP) (id, SEL, ….)
類中的方法通過映射表把每個選擇子都映射到不同的IMP上。
Objective-C運(yùn)行期系統(tǒng)提供的幾個方法都能夠用來操作這張表。
開發(fā)者可以向其中增加選擇子,也可以改變某選擇子所對應(yīng)的方法實現(xiàn)。
還可以交換兩個選擇子所映射到的指針。
無須修改類,或者編寫子類,只要修改了“方法表”的布局,就會反映到程序中所有的實例上。
想交換方法實現(xiàn),可用下列函數(shù):
void method_exchangeImplementations(Method m1, Method m2)
此函數(shù)的兩個參數(shù)表示待交換的兩個方法實現(xiàn),而方法實現(xiàn)則可以通過下列函數(shù)獲得:
Method class_getInstanceMethod(Class aClass, SEL aSelector)
此函數(shù)根據(jù)給定的選擇子從類中取出與之相關(guān)的方法。
例如:
Method originalMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));
Method swappedMethod = class_getInstanceMethod([NSString class], @selector(uppercaseString));
method_exchangeImplemetations(originalMethod, swappedMethod);
然而在實際應(yīng)用中,像這樣直接交換兩個方法的實現(xiàn)的,意義并不大。
通常,我們新編寫一個方法,在此方法中實現(xiàn)所需要的附加功能,與原有的方法交換。并調(diào)用原有實現(xiàn)。
通過此方法,開發(fā)者可以為那些“完全不知道其具體實現(xiàn)的”黑盒方法增加日志記錄功能,這非常有利于程序的調(diào)試。
不能因為Objective-C有這個特性就一定要用它。若是濫用,反而會令代碼變得不易讀懂且難于維護(hù)。
【要點】
在運(yùn)行期,可以向類中新增或替換選擇子所對應(yīng)的方法實現(xiàn)。
使用另一份實現(xiàn)來替換原有的方法實現(xiàn),這道工序叫做“方法調(diào)配”,開發(fā)者通常用此技術(shù)向原有視線中添加新功能。
一般來說,只有調(diào)試程序的時候才需要在運(yùn)行期修改方法實現(xiàn),這種做法不宜濫用。