/**
总结:
在一个函数找不到时,OC提供了三种方式去补救:
1、调用resolveInstanceMethod给个机会让类添加动态添加方法去实现这个函数
2、调用forwardingTargetForSelector让别的对象去执行这个函数
3、调用forwardInvocation(函数执行器)灵活的将目标函数以其他形式执行。用methodSignatureForSelector先偷换方法的签名
如果都不中,调用doesNotRecognizeSelector抛出异常。
*/
#import"ViewController.h"
#import"Person.h"
#import"NSObject+Runtime.h"
@interfaceViewController()
@property(nonatomic,strong)Person*person;
@end
@implementationViewController
- (void)viewDidLoad {
[superviewDidLoad];
_person= [Personalloc];
[_personperformSelector:@selector(shaleba)];
}
@end
#import"Person.h"
#import
@implementationPerson
#pragma mark - crush最先调用,可以在这里用运行时交换方法
+(BOOL)resolveInstanceMethod:(SEL)sel{
if(sel ==@selector(shaleba)) {
class_addMethod([selfclass],sel,class_getMethodImplementation(self,@selector(startEngine)),"@@:");
returnNO;
}
return[superresolveInstanceMethod:sel];
}
//上面👆替换的方法
- (void)startEngine {
NSLog(@"我执行后就不会再崩溃了");
}
#pragma mark - crush第二调用,可以在这里直接指定一个对象去调用
-(id)forwardingTargetForSelector:(SEL)aSelector{
self.stu= [studentnew];
returnself.stu;
}
#pragma mark - crush第3接锅侠之一这个是和下面方法一起使用的,可以在这里直接指定一个对象去调用
-(NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector{
self.stu= [studentnew];
NSMethodSignature*signature =nil;
//如果自己变身的类有改方法,则偷取出方法的签名
if([self.stumethodSignatureForSelector:aSelector]) {
//OC所有的底层方法至少有两个参数第一个参数方法的对象self第二个参数:方法的自身我们人工手写的方法参数是从第三个开始
signature = [self.stumethodSignatureForSelector:aSelector];
}
else{
signature = [supermethodSignatureForSelector:aSelector];
}
returnsignature;
}
#pragma mark - crush第3接锅侠之一这个是和下面方法一起使用的,可以在这里直接指定一个对象去调用
//2.再检查方法的实现(调用方法)
- (void)forwardInvocation:(NSInvocation*)invocation{
//设置方法的执行者为自己copy的哪一个类,必须设置不然不会调用此方法
[invocationsetTarget:self.stu];
//动态拦截参数,修改参数
if([self.stuisKindOfClass:NSClassFromString(@"Teacher")]) {
//argument:参数的地址index:参数的下标
NSString*s =@"呵呵,被改了";
[invocationsetArgument:&satIndex:2];
}
//让自己的替身类去调用方法
[invocationinvoke];
}
@end