如果一个类接收到一个没有实现的实例方法,就会进到下面这个方法里+(BOOL)resolveInstanceMethod:(SEL)sel
,同理类方法会进到这个+(BOOL)resolveClassMethod:(SEL)sel
。
1. 验证
首先创建一个Student类然后调用一个没实现的learn方法:
Student *s = [[Student alloc] init];
[s performSelector:@selector(learn)];
然后在Student.m中拦截一下,打印方法名:
// 如果该类接收到一个没有实现的实例方法,就会进到这个方法里
+(BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"%@",NSStringFromSelector(sel));
return [super resolveInstanceMethod:sel];
}
打印
runtime_动态添加方法[2398:288368] learn
2. 动态添加方法
接下来,拦截到以后动态添加一个方法,用runtime的class_addMethod方法
// 动态添加方法
/*
1、cls 给哪个类添加方法
2、SEL 方法编号
3、IMP 函数指针
4、返回值类型
*/
class_addMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>, <#IMP _Nonnull imp#>, <#const char * _Nullable types#>)
前边三个参数都好理解,关于第四个参数,苹果的文档里有介绍,command+shift+0打开文档,搜索class_addMethod,下面是示例代码和截图
class_addMethod([self class], @selector(resolveThisMethodDynamically), (IMP) myMethodIMP, "v@:");
然后在resolveInstanceMethod动态添加一个方法,在用speak方法接收处理:
+(BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"%@",NSStringFromSelector(sel));
// 动态添加方法
class_addMethod(self, sel, (IMP)speak, "v@:");
return [super resolveInstanceMethod:sel];
}
void speak() {
NSLog(@"speak");
}
打印
runtime_动态添加方法[2430:292288] learn
runtime_动态添加方法[2430:292288] speak
动态创建方法成功
3. 传参数
OC的方法调用,会默认传递两个隐式参数给IMP(方法实现),文档里有示例代码,截图
首先调用时传个参数:
Student *s = [[Student alloc] init];
[s performSelector:@selector(learn) withObject:@"English"];
也可以换成发送消息(这里不懂可以看我之前的关于消息转发的简书),这里的参数和我们接收时的参数是一一对应的,便于理解
Student *s = [[Student alloc] init];
objc_msgSend(s, sel_registerName("learn"), @"English");
这里class_addMethod函数的第四个参数需要改成"v@:@",接收的speak函数需要把隐式参数都加上,才可以收到我们传的参数:
// 如果该类接收到一个没有实现的实例方法,就会进到这个方法里
+(BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"%@",NSStringFromSelector(sel));
// 动态添加方法
class_addMethod(self, sel, (IMP)speak, "v@:@");
return [super resolveInstanceMethod:sel];
}
/**
OC的方法调用,会默认传递两个隐式参数给IMP(方法实现)
objc_msgSend(self,_cmd)
id self 方法的调用者
SEL _cmd 方法编号
*/
void speak(id class, SEL sel, NSString *objc) {
NSLog(@"%@-%@-%@",NSStringFromClass([class class]),NSStringFromSelector(sel),objc);
}
打印
runtime_动态添加方法[2458:295930] learn
runtime_动态添加方法[2458:295930] Student-learn-English
对照一下发消息和接收函数的参数:
// 发消息
objc_msgSend(s, sel_registerName("learn"), @"English");
// 接收函数
void speak(id class, SEL sel, NSString *objc)