消息转发流程图
image
向一个对象发送消息时, 首先会在对象类的cache,method list以及父类对象的cache,method list依次查找SEL对应的IMP
如果没有找到,并且实现了动态方法决议机制就会决议。如果没有实现动态决议机制或者决议失败且实现了消息转发机制。就会进入消息转发流程。否则程序Crash.
进入消息转发 objc——msgSend(id, SEL,...)来实现消息转发
动态方法决议
字面上我的理解是:消息在发送过程中进行判断到底这消息该由谁接收
一:询问是否有动态添加方法来进行处理
Objective C 提供了一种名为动态方法决议的手段,使得我们可以在运行时动态地为一个 selector 提供实现。我们只要实现
代码语言:javascript复制 resolveInstanceMethod:
resolveClassMethod:
并在其中为指定的 selector 提供实现即可(通过调用运行时函数 class_addMethod 来添加)。这两个方法都是 NSObject 中的类方法 实现
代码语言:javascript复制//People.m
void speak(id self, SEL _cmd){
NSLog(@"Now I can speak.");
}
(BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"resolveInstanceMethod: %@", NSStringFromSelector(sel));
if (sel == @selector(speak)) {
class_addMethod([self class], sel, (IMP)speak, "V@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
(BOOL)resolveClassMethod:(SEL)name
{
NSLog(@" >> Class resolving %@", NSStringFromSelector(name));
return [super resolveClassMethod:name];
}
二:询问有没有别人能够帮忙处理一下
代码语言:javascript复制 - (id)forwardingTargetForSelector:(SEL)aSelector
使用如下
代码语言:javascript复制- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"forwardingTargetForSelector: %@", NSStringFromSelector(aSelector));
Bird *bird = [[Bird alloc] init];
if ([bird respondsToSelector:aSelector]) {
return bird;
}
return [super forwardingTargetForSelector: aSelector];
}
// Bird.m
- (void)fly {
NSLog(@"I am a bird, I can fly.");
}
三:在一和二中都没能处理掉时会进入
代码语言:javascript复制- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"forwardInvocation: %@", NSStringFromSelector([anInvocation selector]));
if ([anInvocation selector] == @selector(code)) {
Monkey *monkey = [[Monkey alloc] init];
[anInvocation invokeWithTarget:monkey];
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSLog(@"method signature for selector: %@", NSStringFromSelector(aSelector));
if (aSelector == @selector(code)) {
return [NSMethodSignature signatureWithObjCTypes:"V@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
END
最后消息未能处理的时候,还会调用到
代码语言:javascript复制- (void)doesNotRecognizeSelector:(SEL)aSelector
我们也可以在这个方法中做些文章,避免掉crash,但是只建议在线上环境的时候做处理,实际开发过程中还要把异常抛出来