《Effective Objective-C 2.0 》 阅读笔记 item13

第13条:用“方法调配技术”调试“黑盒方法”

1. 方法调配技术

既不需要源代码,也不需要通过继承子类来覆写方法就能改变这个类本身的功能。这样一来,新功能将在本类的所有实例中生效,而不是仅限于覆写了相关方法的那些子类实例。此方案经常称为“方法调配”(method swizzling)。

2. 原理

类的方法列表会把选择器的名称映射到相关的方法实现之上,使得“动态消息派发系统”能够据此找到应该调用的方法。这些方法均以函数指针(IMP)的形式来表示,其原型如下:

id (*IMP)(id, SEL, ...)

NSString类的选择器映射表 图例:

《Effective Objective-C 2.0 》 阅读笔记 item13_第1张图片
Snip20160309_4.png

Objective-C运行时系统提供的几个方法都能够用来操作这种表。开发者可以向其中新增选择器,也可以改变某选择器所对应的方法实现,还可以交换两个选择器所映射的指针。

  • 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 
#import 
#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

总结:通过此方案,开发者可以为那些“完全不知道其具体实现的”黑盒方法增加日志记录功能,这非常有助于程序调试。然而,此做法只在调试程序时有用,禁止滥用。

要点

  • 运行期,可以向类中新增或替换选择器所对应的方法实现。
  • 使用另一份实现来替换原有的方法实现,这道工序叫做“方法调配”,开发者常用此技术向原有实现中添加新功能。
  • 一般来说,只有调试程序的时候才需要在运行期修改方法实现,这种做法不宜滥用。

你可能感兴趣的:(《Effective Objective-C 2.0 》 阅读笔记 item13)