第13條:用“方法調配技術”調試“黑盒方法”
1. 方法調配技術
既不需要源代碼,也不需要通過繼承子類來覆寫方法就能改變這個類本身的功能咬荷。這樣一來,新功能將在本類的所有實例中生效轻掩,而不是僅限于覆寫了相關方法的那些子類實例幸乒。此方案經常稱為“方法調配”(method swizzling)。
2. 原理
類的方法列表會把選擇器的名稱映射到相關的方法實現之上唇牧,使得“動態(tài)消息派發(fā)系統(tǒng)”能夠據此找到應該調用的方法罕扎。這些方法均以函數指針(IMP)的形式來表示,其原型如下:
id (*IMP)(id, SEL, ...)
NSString類的選擇器映射表 圖例:
Objective-C運行時系統(tǒng)提供的幾個方法都能夠用來操作這種表。開發(fā)者可以向其中新增選擇器奋构,也可以改變某選擇器所對應的方法實現,還可以交換兩個選擇器所映射的指針拱层。
- Method class_getInstanceMethod(Class aClass, SEL aSelector) :根據給定的選擇器從類中取出與之相關的方法弥臼。
- void method_exchangeImplementations( Method m1, Method m2):可以交換兩個方法實現
3. 應用案例
編寫一個方法,在此方法中實現所需的附加功能根灯,并調用原有實現径缅。
分類 NSString (EOCMyAdditions)頭文件:
@interface NSString (EOCMyAdditions)
- (NSString*)eoc_myLowercaseString;
@end
分類 NSString (EOCMyAdditions)實現文件:
@implementation NSString (EOCMyAdditions)
// 新增一個方法,實現附加功能
- (NSString *)eoc_myLowercaseString{
NSString *lowercase = [self eoc_myLowercaseString];
NSLog(@"%@ => %@", self, lowercase);
return lowercase;
}
@end
main函數:
#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.調用lowercaseString方法烙肺,但實現的功能卻是分類中新增方法的附加功能
NSString *string = @"THIs is tHe StRiNg";
NSString *lowercaseString = [string lowercaseString];
}
return 0;
}
輸出結果:
THIs is tHe StRiNg => this is the string
總結:通過此方案纳猪,開發(fā)者可以為那些“完全不知道其具體實現的”黑盒方法增加日志記錄功能,這非常有助于程序調試桃笙。然而氏堤,此做法只在調試程序時有用,禁止濫用搏明。
要點
- 在運行期鼠锈,可以向類中新增或替換選擇器所對應的方法實現。
- 使用另一份實現來替換原有的方法實現星著,這道工序叫做“方法調配”购笆,開發(fā)者常用此技術向原有實現中添加新功能。
- 一般來說虚循,只有調試程序的時候才需要在運行期修改方法實現同欠,這種做法不宜濫用。