f的类型判断来避免。
可以比较下交换前后,
交换前:
SwizzleClassTest_swi_viewDidLoadSel -> SwizzleClassTest_swi_viewDidLoadImp
UIViewController_viewDidLoadSel -> UIViewController_viewDidLoadImp
交换后:
SwizzleClassTest_swi_viewDidLoadSel -> SwizzleClassTest_swi_viewDidLoadImp
UIViewController_swi_viewDidLoadSel -> UIViewController_viewDidLoadImp
UIViewController_viewDidLoadSel -> UIViewController_swi_viewDidLoadImp
可以看出 SwizzleClassTest 没有受影响,映射关系不变。
这种想取消的话,也很简单method_exchangeImplementations
最后补充一点:C函数 实现交换
这里讲的是用C函数交换系统类的方法。而不是fishhook的hook C的函数,目标不一样。原理也不一样
还以hook UIViewController
的viewDidLoad
为例
上面说到,oc方法调用会转换为objc_msgSend(self,_cmd,param)
这种形式,这里再补充一点,objc_msgSend找到imp函数指针后,最终会是imp(self,_cmd,param)
调用C函数,imp其实就是个C函数指针。
那么我们可以定义一个C函数,让sel和我们新建的C函数(imp)形成映射。另外还需要记录之前的imp实现,可以定义一个函数指针来保存sel之前的imp实现;大概示意:
之前:
pOriImp = NULL
vcSel -> vcImp
Cfun(){};
之后:
pOriImp = vcImp;
vcSel -> cFun;// 函数名即为函数指针
详细如下:
/// 准备1. 定义一个函数指针,用于记录系统原本的IMP实现,并初始化为NULL
void (*origin_test_viewDidload)(id,SEL) = NULL;
/// 准备2. 定义要交换的函数,里面会调用系统的IMP
static void swizzle_test_viewDidload(id self, SEL _cmd)
{
// 这里打印的self为UIViewController或者子类实例
NSLog(@"%@",self);
if (origin_test_viewDidload) {
origin_test_viewDidload(self, _cmd);
}
}
/// 开始交换。startHook可以是某个类的方法或实例方法或C函数都可以
+ (void)startHook {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class target = [UIViewController class];
SEL oriSel = @selector(viewDidLoad);
// 要交换的函数
IMP swiImp = (IMP)swizzle_test_viewDidload;
Method origMethod = class_getInstanceMethod(target, oriSel);
// 替换之前的先保留
origin_test_viewDidload = (void *)method_getImplementation(origMethod);
if (origin_test_viewDidload) {
// 最后替换,这里用到了set
method_setImplementation(origMethod, swiImp);
}
});
}
这种hook,没有给类的MethodList新增Method,只是替换了实现,对原类改动最小。
和其它hook方式一样,这种对第三方库 的hook,也是不影响。如果第三方库也交换了,均会得到调用
最后,如果你想取消hook,很简单,method_setImplementation为原来的IMP即可。记着把origin_test_viewDidload也置为NULL.
总结
- 首先要知道方法交换的原理;
- 熟悉它常用接口;
- 被交换方法不存在引发的 父类、子类问题;
- 以及oc中方法的继承、“覆盖”问题;
- 可能引发重复交换的问题,以及后果;
- 理解self只是个隐藏参数,并不一定是当前方法所在的类的实例对象
最后,大概三类hook,至于想用哪种,其实无所谓了,看具体场景。但是原理一定要清楚,每次hook时,都要认真推演一遍,计算下可能产生的影响。