OC由于运行时特性,可以在运行期间动态添加方法,这个寻找动态添加的方法的过程就是动态消息转发。
iOS的消息转发机制分为三个步骤:动态方法解析、快速消息转发机制、标准消息转发机制

(一)动态方法解析
首先是征询接收者所属的类,看其是否能动态添加调用的方法,来处理当前这个未知的选择子;
-(BOOL)resolveInstanceMethod:(SEL)selector或者+(BOOL)resolveClassMethod:(SEL)selector;让开发者有机会提供一个函数实现;若返回YES,说明已提供实现,那运行时系统就会重新启动一次消息发送的过程若为NO,则进入消息转发阶段-快速消息转发
void dynamicMethodIMP(id self, SEL _cmd) {
NSLog(@" >> dynamicMethodIMP");
}
+ (BOOL)resolveInstanceMethod:(SEL)selector
{
NSLog(@" >> Instance resolving %@", NSStringFromSelector(selector));
if (name == @selector(MissMethod)) {
class_addMethod([self class], selector, (IMP)dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:name];
}
(二)快速消息转发
寻找是否在其他对象内有该方法实现,并将该消息转发给这个对象-(id)forwardingTargetForSelector:(SEL)selector ,如果目标对象实现了该方法,Runtime这时就会调用这个方法,给你把这个消息转发给其他对象的机会.只要这个方法返回的不是nil和self,整个消息发送的过程就会被重启,当然返回的对象会变成return的对象,否则就会继续nurmal fowarding
- (id)forwardingTargetForSelector:(SEL)aSelector
{
TargetObj *obj = [[TargetObj alloc]init];
if ([obj respondsToSelector:aSelector]) {
return doctor;
}
return nil;
}
(三)标准消息转发(normal forwarding)
这一步是消息转发的最后一步,首先会发送- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 消息获得函数的参数和返回值,如果返回nil,runtime则会发出doesNotRecognizeSelector消息,然后crash,若是返回了一个函数签名,runtime就会创建一个NSInvocation对象并发送- (void)forwardInvocation:(NSInvocation *)Invocation 消息给目标对象
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[JaySwizzler swizzleSelector:@selector(methodSignatureForSelector:) onClass:[NSObject class] swizzleTargetSel:@selector(user_methodSignatureForSelector:) classType:NO];
[JaySwizzler swizzleSelector:@selector(forwardInvocation:) onClass:[NSObject class] swizzleTargetSel:@selector(user_forwardInvocation:) classType:NO];
});
}
- (NSMethodSignature *)user_methodSignatureForSelector:(SEL)aSelector{
NSMethodSignature *ms = [self user_methodSignatureForSelector:aSelector];
if (ms){
return ms;
}
else{
return [[self class] instanceMethodSignatureForSelector:@selector(noSelector)];
}
}
- (void)noSelector{
NSLog(@"完啦完啦没有方法");
}
- (void)user_forwardInvocation:(NSInvocation *)anInvocation{
@try {
[self user_forwardInvocation:anInvocation];
} @catch (NSException *exception) {
//捕获异常,根据exception打印出堆栈信息,同时也避免了程序崩溃
NSLog(@"我要上报");
NSLog(@"%@%@%@",exception.name,exception.reason,exception.userInfo);
[JayStackLogManager outputStackLog];
//上报
} @finally {
}
}