众所周知,我们UIKit中的 UIViewController类 有一个重要对象方法
- (void)viewWillAppear:(BOOL)animated;
苹果公司没有将其方法的实现源码开源, 假设我们想改变他的方法实现,除了继承它重写、和借助类别重名方法暴力抢先之外,还有其他方法吗?
那就是 Method Swizzling。
1. Method Swizzling 原理
在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现。
每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的Method实现。
我们可以利用
- OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2)
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
来交换2个方法中的IMP,
我们可以利用
- OBJC_EXPORT IMP method_setImplementation(Method m, IMP imp)
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
来直接设置某个方法的IMP
............等等。
实际上就是,偷换了方法的IMP,如下图所示:
2.利用Method Swizzling 实现自己的 viewWillAppear方法。
第一步:给自己的UIViewController加自己的viewWillApper
第二步:调换IMP
#import "UIViewController+Swizzling.h"
#import <objc/runtime.h>
@implementation UIViewController (Swizzling)
+ (void)load
{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(myViewWillAppear:);
Method ori_Method = class_getInstanceMethod(class, originalSelector);
Method my_Method = class_getInstanceMethod(class, swizzledSelector);
method_exchangeImplementations(ori_Method, my_Method);
}
- (void)myViewWillAppear:(BOOL)animated{
[self myViewWillAppear:animated];
NSLog(@"viewWillAppear: %@", self);
}
@end
这个时候运行程序:此时在 + (void)load方法的实现里,交换了viewWillAppear:和myViewWillAppear:的实现。 程序中viewController在表面上是调用viewWillAppear:,实际上调用的myViewWillAppear:方法。这样也就实现了,myViewWillAppear:方法对ViewWillAppear:方法的拦截(就可以在myViewWillAppear:的实现里写一写关于埋点之类的代码...或等等...),接下来在myViewWillAppear:的实现代码里,调用 [self myViewWillAppear:animated];实际上是在调用ViewController的ViewWillAppear:方法。如果UIViewController子类如果不实现ViewWillAppear:则默认找其父类的ViewWillAppear:......以此类推直至找到UIViewController的ViewWillAppear:方法。。。UIViewController的ViewWillAppear:方法的具体实现是不开源的。
3.Method Swizzling 的封装。
第一步:创建NSObject的分类 NSObject+swizzling
第二步:在NSObject+swizzling.h声明自己的类方法,这里我暂时写四个方法...
+ (BOOL)overrideMethod:(SEL)originalSel withMethod:(SEL)swizzledSel;
+ (BOOL)exchangeMethod:(SEL) originalSel withMethod:(SEL) swizzledSel;
+ (BOOL)overrideClassMethod:(SEL) originalSel withClassMethod:(SEL) swizzledSel;
+ (BOOL)exchangeClassMethod:(SEL) originalSel withClassMethod:(SEL) swizzledSel;
第三步:在NSObject+swizzling.m实现自己的类方法:
+ (BOOL)overrideMethod:(SEL)originalSel withMethod:(SEL)swizzledSel
{
Method originalMethod =class_getInstanceMethod(self, originalSel);
if (!originalMethod) {
}
Method swizzledMethod =class_getInstanceMethod(self, swizzledSel);
if (!swizzledMethod) {
}
method_setImplementation(originalMethod, class_getMethodImplementation(self, swizzledSel));
return YES;
}
+ (BOOL)overrideClassMethod:(SEL)originalSel withClassMethod:(SEL)swizzledSel
{
Class c = object_getClass((id)self);
return [c overrideMethod:originalSel withMethod:swizzledSel];
}
+ (BOOL)exchangeMethod:(SEL)originalSel withMethod:(SEL)swizzledSel
{
Method originalMethod =class_getInstanceMethod(self, originalSel);
if (!originalMethod) {
}
Method swizzledMethod =class_getInstanceMethod(self, swizzledSel);
if (!swizzledMethod) {
}
method_exchangeImplementations(class_getInstanceMethod(self, originalSel),class_getInstanceMethod(self, swizzledSel));
return YES;
}
+ (BOOL)exchangeClassMethod:(SEL)originalSel withClassMethod:(SEL)swizzledSel
{
Class c = object_getClass((id)self);
return [c exchangeMethod:originalSel withMethod:swizzledSel];
}
晚安,好梦。