「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」
方法交换
在运行时,类维护了一个方法列表,来解决消息的正确处理。方法列表会把方法的名称映射到相关方法的实现上,其中键值是这个方法的名字Selector(SEL),值是指向这个方法实现的函数指针Implementation(IMP)。
在 OC 的 Runtime 特性中,调用一个对象的方法就是给这个对象发送消息。通过查找接收消息对象的方法列表,从方法列表中查找对应的 SEL,这个 SEL 对应着一个 IMP,通过这个 IMP 找到对应的方法调用。方法交换修改了类的方法列表中SEL和IMP的对应关系,使得已经存在的 SEL 映射了另一个IMP
方法交换用法
-
关于
+(void)initialize和+ (void)load+(void)initialize:当类第一次被调用的时候就会调用该方法,整个程序运行中只会调用一次+ (void)load:当程序启动的时候就会调用该方法,换句话说,只要程序一启动就会调用load方法,整个程序运行中只会调用一次
-
先给要交换的方法的类添加一个 Category,然后在 Category 的
+ (void)load这个方法中添加方法交换 -
获取方法地址
Method class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name) -
交换方法地址,相当于交换实现方式
void method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
方法交换示例
使用场景:需要扩张一个功能又不想改变原始代码可以使用方法交换
先给要交换的方法的类添加一个Category
@interface NSURL (ExchangeMethod)
@end
#import "NSURL+ExchangeMethod.h"
#import <objc/runtime.h>
@implementation NSURL (ExchangeMethod)
//当这个类被加载时调用
+ (void)load{
NSLog(@"load");
//获取方法地址
Method urlWithStr = class_getClassMethod([NSURL class], @selector(URLWithString:));
Method XYURLWithStr = class_getClassMethod([NSURL class], @selector(XYURLWithStr:));
//交换方法地址,相当于交换实现方式
method_exchangeImplementations(urlWithStr,XYURLWithStr);
}
//不能在分类中重写系统方法URLWithString,因为会把系统的功能给覆盖掉,而且分类中不能调用super.
+ (instancetype)XYURLWithStr:(NSString*)str{
/**
1. 此时调用的方法 'XYURLWithStr' 相当于调用系统的 'URLWithString' 方法,原因是在load方法中进行了方法交换.
2. 注意:此处并没有递归操作.
*/
NSURL *url = [NSURL XYURLWithStr:str];//调用系统方法实现
if (url == nil){
NSLog(@"执行自定义的链接");
}
return url;
}
@end
函数调用
#import "ViewController.h"
#import "NSURL+ExchangeMethod.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:@"http://www.baidu.com/错误"];
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url];
}
@end