Objective-C 对象收到消息之后,调用何种方法需要在运行期才能解析处理啊。给定选择子名称相对饮给的方法,可以在运行期改变。(线功能可在本类的所有实例中生效,而不是仅限于覆写了相关方法的那些子类实例)此方案经常成为“方法调配”
类的方法列表会把选择子的名称映射到相关的方法实现上,使得“动态消息派发系统”能够据此找到应该调用的方法。这些方法均以函数指针形式来表示,这种指针叫IMP,其原型如下
id (* IMP)(id SEL, ...)
NSString 类可以响应 lowercaseString(小写字母),uppercaseString(大写字母)/capitalizedString 等选择子。这张映射表中的每个选择子都映射到不同的IMP之上。
图 object_20191120_001.png
P53
Objective-C运行期系统提供了几个方法能都操作这张表。开发者可以向其中新增选择子,也可以改变选择子所对应的方法实现,还可以交换两个选择子映射到的指针。
图 object_20191120_002.png
上述修改无需编写子类,只是修改了“方发表”的布局,就会放映到程序中所有的NSString实例上。
互换两个方法实现
void method_exchangeImplementations(Method m1, Method m2);
此函数的两个参数表示待交互的两个方法实现,而方法实现则可通过下列函数获得
Method class_getInstanceMethod(Class aClass, SEL aSelector)
此函数根据给定的选择从类中取出与之想关的方法。执行下列代码,即可交换前面提到的lowerecaseString 与 uppercaseString 方法实现
Method originalMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));
Method swappedMethod = class_getInstanceMethod([NSString class], @selector(uppercaseString));
method_exchangeImplementations(originalMethod, swappedMethod);
从现在开始,如果在NSString 实例上调用 lowercaseString,那么执行的将是 uppercaseString 的原有实现
这样直接交换方法的实现,意义并不大。但是,可以通过这一手段为即有的方法实现增添新功能。
例: 在调用 lowercaseString 时记录某些信息,这是就可以通过交换方法实现来达成次目标。我们新便写一个方法,在此方法中实现所需的附加功能,并调用原有实现。
新方法可以添加至NSString 的“分类”(category)中:
@interface NSString (LJLMyAdditions)
-(NSString *)ljl_myLowercaseString;
@end
将原有的 lowercaseString(小写字母) 与 ljl_myLowercaseString 方法交换。新方法的实现代码可以这样写
@implementation NSString (LJLMyadditions)
-(NSString *)ljl_myLowercaseString{
NSString * lowercase = [self lowercaseString];
NSLog(@"%@ => %@",self, lowercase);
return lowercase;
}
@end
看上去像是递归调用死循环,但是因为这个方法是要和 lowercaseString 方法互换的,所以在运行期,ljl_myLowercaseString 选择子实际上对应于原有的 lowercaseString 方法实现。最后,通过下列代码来交换着两个方法实现:
Method originalMethod = class_getInstanceMethod(NSString class, @selector(lowercaseString));
Method swappedMethod = class_getInstanceMethod(NSString class, @selector(ljl_myLowercaseString));
method_exchangeImplementations(originalMethod, swappedMethod);
再执行 lowercaseString 就会输出一行记录消息
一般通过此方案给不公开的方法(黑盒方法)增加日志记录功能,有助于调试。
重点:
1、在运行期,可以向勒种新增或替换选择子所对应的方法实现。
2、使用另一份实现来替换原有的方法实现,这道工序叫做“方法调配”,开发者常用此技术向原有实现中添加新功能
3、一般来说,只有调试程序的时候才需要在运行期修改方法实现,这种做法不宜滥用。
参考:Effective objective-C2.0