一、介绍:
系统中SEL(方法编号)与IMP(方法实现)是一对一的映射关系,方法交换的中心思想是对SEL与IMP进行处理。由此可见有两种方案:
- 交换SEL,使系统SEL对应新的方法的IMP。
- 交换IMP,使新的方法IMP对应系统方法的SEL。
二、上代码:
2.1 交换SEL
1)首先对需要用到的方法进行简单说明:
a. 根据方法编号从类中取出方法:
@param cls 获取方法类
@param name 方法编号
class_getClassMethod(Class _Nullable cls, SEL _Nonnull name) - 取出类方法
class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name) -取出实例方法
b.交换SEL
@param m1 系统方法
@param m2 新方法
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
c.给类动态添加方法
@param cls 需要添加方法的类
@param SEL 添加的方法编号
@param IMP 添加的方法的实现
@param types 对新方法的返回值及参数类型做的标识
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types)
2)应用场景:
需求如下:在调用以下系统方法的时候,对URLString进行校验,判断是否为nil,并提示输出。
- (nullable instancetype)URLWithString:(NSString *)URLString;
第一种方法:创建一个NSURL的分类,在分类中创建新的类方法,在load方法中进行方法的交换。
创建分类:NSURL+HookUrl
#import "NSURL+HookUrl.h"
#import <objc/message.h>
#import <objc/runtime.h>
@implementation NSURL (HookUrl)
+(void)load{
Method m1 = class_getClassMethod(self, @selector(URLWithString:)); - 系统方法
Method m2 = class_getClassMethod(self, @selector(HKURLWithString:));-新方法
method_exchangeImplementations(m1, m2);-交换
}
+ (nullable instancetype)HKURLWithString:(NSString *)URLString{
// 因为进行了方法的交换,所以HKURLWithString对应的IMP是原系统方法实现。若使用URLWithString,会形成递归。
NSURL *url = [NSURL HKURLWithString:URLString];
if (url == nil) {
NSLog(@"DDDDD");
}
return url ;
}
@end
第二种方法:创建一个hook类,进行方法的交换。
@implementation Hook
+(void)load{
Method m1 = class_getClassMethod(NSURL.class, @selector(URLWithString:)); - 系统方法
class_addMethod(object_getClass(NSURL.class), @selector(HKURLWithString:), (IMP)newMethod,"@@:@"); // 添加的是类方法
//交换: SEL - IMP 映射
method_exchangeImplementations(m1, class_getClassMethod(NSURL.class, @selector(HKURLWithString:)));
}
void * newMethod(id self,SEL _cmd, NSString *str){
NSURL *url = [[NSURL class] performSelector:@selector(HKURLWithString:) withObject:str];
if (url == nil) {
NSLog(@"DDDDD");
}
return (__bridge void *)(url) ;
}
2.2 交换IMP
1)首先对需要用到的方法进行简单说明:
更改IMP
@param m 方法
@param name imp 方法实现
method_setImplementation(Method _Nonnull m, IMP _Nonnull imp)
2)应用场景:
需求如下:在调用以下系统方法的时候,对URLString进行校验,判断是否为nil,并提示输出。
- (nullable instancetype)URLWithString:(NSString *)URLString;
@implementation Hook
+(void)load{
Method m1 = class_getClassMethod(NSURL.class, @selector(URLWithString:));- 系统方法
IMP swizzledMethod =(IMP)newMethod;-新的方法实现
IMP methodImp = [NSURL.class methodForSelector:@selector(URLWithString:)];-系统方法的实现
class_addMethod(object_getClass(NSURL.class), @selector(HKURLWithString:), swizzledMethod,"@@:@"); - 添加新方法到NSURL类
Method m2 = class_getClassMethod(NSURL.class, @selector(HKURLWithString:)); - 获取新方法
method_setImplementation(m1,swizzledMethod);
method_setImplementation(m2,methodImp);
- 交换IMP
}
void * newMethod(id self,SEL _cmd, NSString *str){
NSURL *url = [[NSURL class] performSelector:@selector(HKURLWithString:) withObject:str];
if (url == nil) {
NSLog(@"DDDDD");
}
return (__bridge void *)(url) ;
}