第13條:用“方法調(diào)配技術(shù)”調(diào)試“黑盒方法”
1. 方法調(diào)配技術(shù)
既不需要源代碼,也不需要通過(guò)繼承子類(lèi)來(lái)覆寫(xiě)方法就能改變這個(gè)類(lèi)本身的功能。這樣一來(lái),新功能將在本類(lèi)的所有實(shí)例中生效,而不是僅限于覆寫(xiě)了相關(guān)方法的那些子類(lèi)實(shí)例。此方案經(jīng)常稱(chēng)為“方法調(diào)配”(method swizzling)。
2. 原理
類(lèi)的方法列表會(huì)把選擇器的名稱(chēng)映射到相關(guān)的方法實(shí)現(xiàn)之上,使得“動(dòng)態(tài)消息派發(fā)系統(tǒng)”能夠據(jù)此找到應(yīng)該調(diào)用的方法。這些方法均以函數(shù)指針(IMP)的形式來(lái)表示,其原型如下:
id (*IMP)(id, SEL, ...)
NSString類(lèi)的選擇器映射表 圖例:

Objective-C運(yùn)行時(shí)系統(tǒng)提供的幾個(gè)方法都能夠用來(lái)操作這種表。開(kāi)發(fā)者可以向其中新增選擇器,也可以改變某選擇器所對(duì)應(yīng)的方法實(shí)現(xiàn),還可以交換兩個(gè)選擇器所映射的指針。
- Method class_getInstanceMethod(Class aClass, SEL aSelector) :根據(jù)給定的選擇器從類(lèi)中取出與之相關(guān)的方法。
- void method_exchangeImplementations( Method m1, Method m2):可以交換兩個(gè)方法實(shí)現(xiàn)
3. 應(yīng)用案例
編寫(xiě)一個(gè)方法,在此方法中實(shí)現(xiàn)所需的附加功能,并調(diào)用原有實(shí)現(xiàn)。
分類(lèi) NSString (EOCMyAdditions)頭文件:
@interface NSString (EOCMyAdditions)
- (NSString*)eoc_myLowercaseString;
@end
分類(lèi) NSString (EOCMyAdditions)實(shí)現(xiàn)文件:
@implementation NSString (EOCMyAdditions)
// 新增一個(gè)方法,實(shí)現(xiàn)附加功能
- (NSString *)eoc_myLowercaseString{
NSString *lowercase = [self eoc_myLowercaseString];
NSLog(@"%@ => %@", self, lowercase);
return lowercase;
}
@end
main函數(shù):
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "NSString+EOCMyAdditions.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 1.交換方法
Method originalMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));
Method swappedMethod = class_getInstanceMethod([NSString class], @selector(eoc_myLowercaseString));
// 2.交換方法
method_exchangeImplementations(originalMethod, swappedMethod);
// 3.調(diào)用lowercaseString方法,但實(shí)現(xiàn)的功能卻是分類(lèi)中新增方法的附加功能
NSString *string = @"THIs is tHe StRiNg";
NSString *lowercaseString = [string lowercaseString];
}
return 0;
}
輸出結(jié)果:
THIs is tHe StRiNg => this is the string
總結(jié):通過(guò)此方案,開(kāi)發(fā)者可以為那些“完全不知道其具體實(shí)現(xiàn)的”黑盒方法增加日志記錄功能,這非常有助于程序調(diào)試。然而,此做法只在調(diào)試程序時(shí)有用,禁止濫用。
要點(diǎn)
- 在運(yùn)行期,可以向類(lèi)中新增或替換選擇器所對(duì)應(yīng)的方法實(shí)現(xiàn)。
- 使用另一份實(shí)現(xiàn)來(lái)替換原有的方法實(shí)現(xiàn),這道工序叫做“方法調(diào)配”,開(kāi)發(fā)者常用此技術(shù)向原有實(shí)現(xiàn)中添加新功能。
- 一般來(lái)說(shuō),只有調(diào)試程序的時(shí)候才需要在運(yùn)行期修改方法實(shí)現(xiàn),這種做法不宜濫用。