在上篇文章中咱们讲到,若是方法查找和动态方法解析都没有找到方法实现,那么就会来到消息转发流程。此次咱们就来研究一下消息转发流程。bash
其实想要找到消息转发流程是件不简单的事,最直接的办法就是看汇编了,不过还好有前辈们探索过,在这里咱们也能够借鉴一下。咱们在探索方法查找流程的时候有看到一个方法:函数
/***********************************************************************
* log_and_fill_cache
* Log this method call. If the logger permits it, fill the method cache.
* cls is the method whose cache should be filled.
* implementer is the class that owns the implementation in question.
**********************************************************************/
static void
log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer)
{
#if SUPPORT_MESSAGE_LOGGING
if (objcMsgLogEnabled) {
bool cacheIt = logMessageSend(implementer->isMetaClass(),
cls->nameForLogging(),
implementer->nameForLogging(),
sel);
if (!cacheIt) return;
}
#endif
cache_fill (cls, sel, imp, receiver);
}
复制代码
SUPPORT_MESSAGE_LOGGING
默认为1,也就是说只要objcMsgLogEnabled
为true,就能够打印日志信息了,那么它是在哪里设置的呢?ui
咱们跟进去再进行全局搜索能够发现这个函数:this
void instrumentObjcMessageSends(BOOL flag)
{
bool enable = flag;
// Shortcut NOP
if (objcMsgLogEnabled == enable)
return;
// If enabling, flush all method caches so we get some traces
if (enable)
_objc_flush_caches(Nil);
// Sync our log file
if (objcMsgLogFD != -1)
fsync (objcMsgLogFD);
objcMsgLogEnabled = enable;
}
复制代码
当调用这个函数时,objcMsgLogEnabled
会被赋值,那就能够在咱们本身的代码中使用:spa
LGStudent *student = [LGStudent alloc] ;
instrumentObjcMessageSends(true);
[student saySomething];
instrumentObjcMessageSends(false);
复制代码
运行工程后,前往/tmp/
目录,打开最新的msgSends
文件: 3d
文件里有不少方法,其中resolveInstanceMethod
咱们已经分析过了,是动态方法解析,接下来看一下forwardingTargetForSelector
方法,源码以下:日志
+ (id)forwardingTargetForSelector:(SEL)sel {
return nil;
}
- (id)forwardingTargetForSelector:(SEL)sel {
return nil;
}
复制代码
从源码中咱们看不到任何信息,这个时候只能借助官方文档了:code
forwardInvocation:
方法中进行处理。当快速转发流程没有实现,就会来到慢速转发流程,咱们从日志打印中寻找到methodSignatureForSelector:
方法,查看源码:cdn
// Replaced by CF (returns an NSMethodSignature)
+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)sel {
_objc_fatal("+[NSObject instanceMethodSignatureForSelector:] "
"not available without CoreFoundation");
}
// Replaced by CF (returns an NSMethodSignature)
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
_objc_fatal("+[NSObject methodSignatureForSelector:] "
"not available without CoreFoundation");
}
// Replaced by CF (returns an NSMethodSignature)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
_objc_fatal("-[NSObject methodSignatureForSelector:] "
"not available without CoreFoundation");
}
复制代码
一样要找官方文档: 对象
NSMethodSignature
类型的方法签名并返回。光有一个方法签名确定是不行的,因而进入到forwardInvocation:
中,查看源码:
+ (void)forwardInvocation:(NSInvocation *)invocation {
[self doesNotRecognizeSelector:(invocation ? [invocation selector] : 0)];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[self doesNotRecognizeSelector:(invocation ? [invocation selector] : 0)];
}
复制代码
一样查看文档:
methodSignatureForSelector:
方法。anInvocation
将消息发送到对象。调用将保存结果,运行时系统将提取此结果并将其传递给原始发件人。若是在这个方法中也没能找到方法实现,那么就会跳到doesNotRecognizeSelector:
中报错而且崩溃了:
// Replaced by CF (throws an NSException)
+ (void)doesNotRecognizeSelector:(SEL)sel {
_objc_fatal("+[%s %s]: unrecognized selector sent to instance %p",
class_getName(self), sel_getName(sel), self);
}
// Replaced by CF (throws an NSException)
- (void)doesNotRecognizeSelector:(SEL)sel {
_objc_fatal("-[%s %s]: unrecognized selector sent to instance %p",
object_getClassName(self), sel_getName(sel), self);
}
复制代码
咱们能够看到这个方法中的报错信息其实就是咱们日常开发中常见的找不到方法的那个错误。
至此,整个消息转发流程也就结束了。