在上篇文章中咱們講到,若是方法查找和動態方法解析都沒有找到方法實現,那麼就會來到消息轉發流程。此次咱們就來研究一下消息轉發流程。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);
}
複製代碼
咱們能夠看到這個方法中的報錯信息其實就是咱們日常開發中常見的找不到方法的那個錯誤。
至此,整個消息轉發流程也就結束了。