公司项目开发要用到runtime来hook相应的方法啦,我会在接下来的时间逐渐实践并完善runtime的相关知识,然后分享给大家,也希望大家能指出我的错误。今天先开个坑(頑張れ)
今天就从runtime swizzling开始吧,swizzling方法的主要目的就是hook方法,实现方法的交换,下面的场景为我们交换页面的进入(viewWillAppear)和离开方法(viewWillDisappear)。
一、首先,我们要创建UIViewController的类别并导入:
#import "UIViewController+Swizzling.h"
#import <objc/runtime.h>//导入运行时库
二、在+load方法中调用相应的方法:
//Swizzling应该在+load方法中实现,因为+load方法可以保证在类最开始加载时会调用。runtime影响范围是全局的,以+load能够保证在类初始化的时候一定会被加载,这可以保证统一性。
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//因为swizzling会改变全局,我们需要在运行时采取相应的防范措施。保证原子操作就是一个措施,确保代码即使在多线程环境下也只会被执行一次。
[self methodSwizzlingWithOriginalSelector:@selector(viewWillAppear:) bySwizzledSelector:@selector(swizzledViewWillAppear:)];
[self methodSwizzlingWithOriginalSelector:@selector(viewWillDisappear:) bySwizzledSelector:@selector(swizzledViewWillDisappear:)];
});
}
三、进行方法的交换处理
*
originalSelector:源方法
swizzledSelector:我们要交换的方法
*/
+ (void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector bySwizzledSelector:(SEL)swizzledSelector{
Class class = [self class];
//1、得到类的方法
Method originalMethod = class_getInstanceMethod(class, originalSelector);
//得到新的类的方法
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
//2、先尝试給源SEL添加IMP,这里是为了避免源SEL没有实现IMP的情况,一般源SEL都是实现的,所以返回NO
BOOL didAddMethod = class_addMethod(class,originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
NSLog(@"-----------didAddMethod:%@",didAddMethod?@"YES":@"NO");
/*
由于源SEL一般都是实现的,万一没有实现,用我们添加的方法替换源SEL。所以,一般直接执行4.
*/
if (didAddMethod) {
//3、添加成功:说明源SEL没有实现IMP,将源SEL的IMP替换到交换SEL的IMP
class_replaceMethod(class,swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
//4、添加失败:说明源SEL已经有IMP,直接将两个SEL的IMP交换即可
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
四、在交换方法中实现相应的功能
#pragma mnark 这里是hook的方法,在此处执行。
- (void)swizzledViewWillAppear:(BOOL)animated{
[self swizzledViewWillAppear:animated];
//在此处获取当前类的类名
NSLog(@"进入页面获取当前类名:%@",NSStringFromClass([self class]));
}
- (void)swizzledViewWillDisappear:(BOOL)animated{
[self swizzledViewWillDisappear:animated];
NSLog(@"离开页面获取当前类名:%@",NSStringFromClass([self class]));
}