运行时的消息转发分三步, 当你调用了没有实现的方法时, 有机会通过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];
}