iOS开发 Method Swizzling

众所周知,我们UIKit中的 UIViewController类 有一个重要对象方法
- (void)viewWillAppear:(BOOL)animated;
苹果公司没有将其方法的实现源码开源, 假设我们想改变他的方法实现,除了继承它重写、和借助类别重名方法暴力抢先之外,还有其他方法吗?
那就是 Method Swizzling。

1. Method Swizzling 原理

在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现。
每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的Method实现。


iOS开发 Method Swizzling_第1张图片
1.png

我们可以利用
- 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,如下图所示:


iOS开发 Method Swizzling_第2张图片
2.png

2.利用Method Swizzling 实现自己的 viewWillAppear方法。

第一步:给自己的UIViewController加自己的viewWillApper
第二步:调换IMP

 #import "UIViewController+Swizzling.h"
 #import 

 @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:方法的具体实现是不开源的。

iOS开发 Method Swizzling_第3张图片
3.png

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];
}

晚安,好梦。

你可能感兴趣的:(iOS开发 Method Swizzling)