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 {
}
}