1、动态方法解析
对象在收到无法处理的消息时,会调用下面的方法,前者是调用类方法时会调用,后者是调用对象方法时会调用。
// 类方法专用
+ (BOOL)resolveClassMethod:(SEL)sel
// 对象方法专用
+ (BOOL)resolveInstanceMethod:(SEL)sel
在该方法中,需要给对象所属类动态的添加一个方法,并返回YES,表明可以处理
void dynamicMethodIMP(id self, SEL _cmd) {
NSLog(@" >> dynamicMethodIMP");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"%@", NSStringFromSelector(sel));
if (sel == @selector(run)) {
class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
2、备援接受者
经历了第一步后,如果该消息还是无法处理,那么就会调用下面的方法,查询是否有其它对象能够处理该消息。
- (id)forwardingTargetForSelector:(SEL)aSelector
在这个方法里,我们需要返回一个能够处理该消息的对象
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"%@", NSStringFromSelector(aSelector));
if (aSelector == @selector(run)) {
Animal *animal = [[Animal alloc] init];
return animal;
}
return [super forwardingTargetForSelector:aSelector];
}
3、完整的消息转发
经历了前两步,还是无法处理消息,那么就会做最后的尝试,先调用methodSignatureForSelector:获取方法签名,然后再调用forwardInvocation:进行处理,这一步的处理可以直接转发给其它对象,即和第二步的效果等效,但是很少有人这么干,因为消息处理越靠后,就表示处理消息的成本越大,性能的开销就越大。所以,在这种方式下,会改变消息内容,比如增加参数,改变选择子等等。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
- (void)forwardInvocation:(NSInvocation *)anInvocation
下面是改变选择子的例子
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSLog(@"%@", NSStringFromSelector(aSelector));
if (aSelector == @selector(run)) {
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:"];
return signature;
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"%@", anInvocation);
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:"]; anInvocation = [NSInvocation invocationWithMethodSignature:signature];
[anInvocation setSelector:@selector(runNew)];
if ([self respondsToSelector:@selector(runNew)]) {
[anInvocation invokeWithTarget:self];
return;
} else {
Animal *s = [[Animal alloc] init];
if ([s respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:s];
return;
}
}
[super forwardInvocation:anInvocation];
}
- (void)runNew {
NSLog(@"runNewrunNewrunNew");
}