import "CXDHook.h"
import <objc/runtime.h>
@implementation CXDHook
-
(void)hookClass:(Class)classObject fromSelector:(SEL)fromSelector toSelector:(SEL)toSelector {
Class class = classObject;
//得到被交换类的实例方法
Method fromMethod = class_getInstanceMethod(class, fromSelector);
//得到交换类的实例方法
Method toMethod = class_getInstanceMethod(class, toSelector);//class_addMethod()函数返回成功表示交换的方法没实现,然后通过class_addMethod()函数先实现;返回失败则表示被交换方法已存在,可以直接进行IMP指针交换
if (class_addMethod(class, fromSelector, method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
//进行方法的交换
class_replaceMethod(class, toSelector, method_getImplementation(fromMethod), method_getTypeEncoding(fromMethod));
} else {
//交换IMP指针
method_exchangeImplementations(fromMethod, toMethod);
}
}
但是,像上面这段代码一样,直接使用Runtime的方法进行方法交换会有很多风险,RSSwizzle库里指出了四个典型的直接使用Runtime方法进行方法交换的风险。
第一个风险是,需要在+load方法中进行方法交换。因为如果在其他时候进行方法交换,难以保证另外一个线程中不会同时调用被交换的方法,从而导致程序不能按预期执行。
第二个风险是,被交换的方法必须是当前类的方法,不能使父类的方法,直接把父类的实现拷贝过来不会起作用。父类的方法必须在调用的时候使用,而不是方法交换时使用。
第三个风险是,交换的方法如果依赖了cmd,那么交换后,如果cmd发生了变化,就会出现各种奇怪问题,而且这些问题还很难排查。特别是交换了系统方法,你无法保证系统方法内部是否依赖了cmd。
第四个风险是,方法交换命名冲突。如果出现冲突,可能会导致方法交换失败。
更安全的方法交换库Aspects
Aspects是一个通过Runtime消息转发机制来实现方法交换的库。它将所有的方法调用都指到_objc_msgForward函数调用上,按照自己的方式实现了消息转发,自己处理参数列表,处理返回值,最后通过NSInvocation调用来实现方法交换。同时Aspects还考虑了一些方法交换可能会引发的风险,并进行了处理。
Aspects实现方法交换的原理:
Aspects的整体流程是,先判断是否可以进行方法交换。这一步会进行安全问题的判断处理。如果没有风险的话,再针对要交换的是类对象还是实例对象分别进行处理。
网友评论