**Method swizzling** 是改变selector(选择器)实现的一个的过程,这个技术依赖于Object-C的方法调用可以通过运行时进行拦截(改变),具体做法就是改变类的调度表(dispatch table)中selector(选择器)到最终函数间的映射关系。
举个例子,假如我们想跟踪每个控制器在某个程序中展示给用户的次数:那么可以在每个控制器的viewDidAppear
的实现里面添加跟踪的代码,但是这样会导致大量的重复代码。通过继承可以解决重复代码的问题,可即使使用继承,在 UIViewController
, UITableViewController
, UINavigationController
和其他类型的 view controller 里面的viewDidAppear方法依然会存在大量重复代码。但幸运的是,我们有Method swizzling
\#import
@implementation UIViewController (Tracking)
+ (void)load {
static dispatch\_once\_t onceToken;
dispatch\_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx\_viewWillAppear:);
Method originalMethod = class\_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class\_getInstanceMethod(class, swizzledSelector);
// When swizzling a class method, use the following:
// Class class = object\_getClass((id)self);
// ...
// Method originalMethod = class\_getClassMethod(class, originalSelector);
// Method swizzledMethod = class\_getClassMethod(class, swizzledSelector);
BOOL didAddMethod =
class\_addMethod(class,
originalSelector,
method\_getImplementation(swizzledMethod),
method\_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class\_replaceMethod(class,
swizzledSelector,
method\_getImplementation(originalMethod),
method\_getTypeEncoding(originalMethod));
} else {
method\_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
\#pragma mark - Method Swizzling
- (void)xxx\_viewWillAppear:(BOOL)animated {
[self xxx\_viewWillAppear:animated];
NSLog(@"viewWillAppear: %@", self);
}
@end````
在计算机科学中,pointer swizzling 就是一种引用的变换,基于位置和名称的指向引用的变换。在原生的Objective-C关于Method swizzling的说明并不是很公开,但Method Swizzling的存在依然很容易理解,因为它能够通过@selector来改变函数指针的引用。
现在,当UIViewController或它子类的任何实例触发viewWillAppear:方法都会打印一条log日志。
向视图控制器的生命周期中注入操作、事件的响应、视图的绘制,或Foundation中的网络堆栈都是能够利用method swizzling产生明显效果的场景。。