我们经常会浏览到关于method swizzling
的文章, 都称之为黑魔法. 但是网络上各路大神的文章都很深奥, 本文我就以自己的理解深入浅出的解析一下这个黑魔法, 力求看了都能理解这个所谓的 "黑魔法"究竟是什么.
最近公司的项目遇到一个需求, 要在每一个viewWillDisappear
时做一个操作, 调用一句代码. 看到这个需求时我就想起了之前遇过的黑魔法method swizzling
;
作用:
首先先看这个method swizzling
是做什么用的?method swizzling
能调换方法名和方法实现之间的对应关系. 对于我的需求来说, 就是每次在Controller里调用viewWillDisappear
的时候, 会执行另一个方法的实现, 在这里称作XXX_viewWillDisappear
. 我调用的方法名是viewWillDisappear
, 其实执行的是XXX_viewWillDisappear
里面的实现, 然后我就可以在另一个实现里做我想做的事情.前提:
Objective-C语言是一门动态语言, 一个方法在编译时并不确定, 只有在执行时才能确定. 消息分发之后才能确定执行者. 这个就是所谓的Runtime机制. 因为这种机制的存在, 必然存在一种消息和执行的对应关系, 即是一个Selector和一个IMP的对应关系. 而method swizzling
的存在其实是调换了这种对应关系.操作:
新建一个UIViewController的分类, 写一个XXX_viewWillDisappear
方法, 这个方法是用来和系统的XXX_viewWillDisappear
方法进行交换的. 然后重写+load
方法, 在+load
方法内部调换方法名与方法实现的对应关系.+load
或+initialize
:
确保在方法调用之前viewWillDisappear
和XXX_viewWillDisappear
的实现已经被调换, 因此在+load
或+initialize
内部执行方法实现的调换都是可以的. 并且由于method swizzling
是全局的交换, 所以应该在 dispatch_once 中完成.代码来源于NSHipster, 并对代码增加了注释.
@implementation UIViewController (swizzling)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillDisappear:); //初始化SEL类型对象指向viewWillDisappear:
SEL swizzledSelector = @selector(XXX_viewWillDisappear:); //初始化SEL类型对象指向XXX_viewWillDisappear:
//viewWillDisappear:对应的Method, 为类方法列表中originalSelector指向的方法实现
Method originalMethod = class_getInstanceMethod(class, originalSelector);
//XXX_viewWillDisappear:对应的Method, 为类方法列表中swizzledSelector指向的方法实现
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
/*
在类中增加方法: 方法名为originalSelector(即viewWillDisappear:),
对应的方法实现为swizzledMethod的实现(即XXX_viewWillDisappear:对应的实现)
*/
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
/*
替换类中方法实现: 方法名为swizzledSelector(即XXX_viewWillDisappear:),
替换为为originalMethod的实现(即viewWillDisappear:对应的实现)
*/
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
//直接调换originalMethod和swizzledMethod方法的实现
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
#pragma mark - Method Swizzling
- (void)XXX_viewWillDisppear:(BOOL)animated {
//在VC中调用`viewWillDisppear`实际是调用了这里的实现
[self XXX_viewWillDisppear:animated]; //这一句代码, 实际上是调用`viewWillDisppear`到实现
NSLog(@"viewWillDisappear: %@", self);
}
@end
参考文章:
http://nshipster.cn/method-swizzling/
http://www.cocoachina.com/ios/20141031/10105.html
http://blog.jobbole.com/45963/
文章整理参考网络文章,如有错误,欢迎讨论指出。