運行時的消息轉發分三步, 當你調用了沒有實現的方法時, 有機會通過runtime的消息轉發機制補救一下
- resolveInstanceMethod/resolveClassMethod 這里可以動態去創建方法來解決Crash
- forwardingTargetForSelector ?????第一步未解決, 就會走到這里, 可以給出一個Target去轉發這個消息(方法調用)
- forwardInvocation ???????上面2步都沒有解決問題, 這里是最后一次機會, 利用methodSignatureForSelector返回一個方法簽名, 在forwardInvocation中轉發給對應的target
實例方法實現參考
+ (BOOL)resolveInstanceMethod:(SEL)sel {if (sel == @selector(testInstance)) {IMP imp = class_getMethodImplementation([self class], @selector(test));return class_addMethod([self class], sel, imp, "v@:");}return NO;
}- (id)forwardingTargetForSelector:(SEL)aSelector {if (aSelector == @selector(testInstance)) {return self.realObj;}return [super forwardingTargetForSelector:aSelector];
}- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {if (aSelector == @selector(testInstance)) {return [[RealTestObject alloc] methodSignatureForSelector:aSelector];}return [super methodSignatureForSelector: aSelector];
}- (void)forwardInvocation:(NSInvocation *)anInvocation {SEL sel = anInvocation.selector;if ([self.realObj respondsToSelector:sel]) {[anInvocation invokeWithTarget:self.realObj];return;}[super forwardInvocation:anInvocation];
}
類方法實現參考
+ (BOOL)resolveClassMethod:(SEL)sel {if (sel == @selector(testClass)) {
//也可以通過block創建一個IMP去替代方法實現
// IMP imp = imp_implementationWithBlock(^(void) {
// NSLog(@"imp_implementationWithBlock");
// });IMP imp = class_getMethodImplementation(objc_getMetaClass("TestObject"), @selector(testLogClass));class_addMethod(objc_getMetaClass("TestObject"), sel, imp, "v@:");return YES;}return NO;
}+ (id)forwardingTargetForSelector:(SEL)aSelector {if (aSelector == @selector(testClass)) {return [RealTestObject class];}return [super forwardingTargetForSelector:aSelector];
}+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {if (aSelector == @selector(testClass)) {return [RealTestObject methodSignatureForSelector:aSelector];}return [super methodSignatureForSelector:aSelector];
}+ (void)forwardInvocation:(NSInvocation *)anInvocation {SEL sel = anInvocation.selector;if (sel == @selector(testClass)) {[anInvocation invokeWithTarget:[RealTestObject class]];return;}[super forwardInvocation:anInvocation];
}