ObjC Runtime簡析-- objc_MsgSend

在ObjC中,方法的調用是經過消息機制依賴runtime來實現的。使用[]給對象發送一個消息,轉化爲C++的實現是調用了objc_msgSend()函數。緩存

objc_msgSend()函數在runtime源碼中是經過彙編代碼實現的。它存在與runtime源碼的這個位置:bash

objc_msgSend()的源碼

經過跟讀源碼咱們發現整個objc_msgSend的調用流程是這樣的:markdown

經過上圖咱們發現objc_msgSend的調用大體分爲三個流程:函數

  • 消息發送
  • 動態方法解析
  • 消息轉發

消息發送

經過上圖咱們能夠看出,消息發送通過了斷定消息接受者是否爲nil,而後從緩存中查找方法,若是依然查找不到會遞歸getMethodNoSuper_nolock查找父類的方法緩存列表和父類的方法列表。 若是整個過程下來都找不到須要調用的方法,就會進如動態方法解析階段。post

動態方法解析

當進行方法的調用的時候若是找不到方法,會按照調用的方法類型去調用相應的動態解析方法,若是調用的是實例方法則會嘗試調用+ (BOOL)resolveInstanceMethod:(SEL)sel方法,若是調用的是類方法,則會嘗試調用+ (BOOL)resolveClassMethod:(SEL)sel方法。咱們能夠在動態解析中嘗試爲該方法添加一個新的已經實現了的方法,以下所示:編碼

// 類方法
+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(test)) {
        Method method = class_getClassMethod(self, @selector(test2));
        class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

+ (void)test2 {
    NSLog(@"%s",__func__);
}

// 實例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
        Method method = class_getInstanceMethod(self, @selector(test2));
        class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

- (void)test2 {
    NSLog(@"%s",__func__);
}

複製代碼

消息轉發

若是消息機制進行到動態方法解析的時候依然找不到須要調用的方法,那麼就會進入消息轉發階段。 消息轉發階段會視調用的方法的類型調用轉發方法,實例方法調用- (id)forwardingTargetForSelector:(SEL)aSelector,類方法則調用+ (id)forwardingTargetForSelector:(SEL)aSelector方法。該方法要求返回一個能夠接受這個消息的對象。 好比Person調用test方法,而Person沒有實現test方法,而Student類實現了這個方法。那麼就能夠將這個消息轉發給Student去處理:spa

// 實例方法
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == @selector(test)) {
        return [[Student alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}

// 類方法
+ (id)forwardingTargetForSelector:(SEL)aSelector
{
        if (aSelector == @selector(test)) {
            return [[Student alloc] init];
        }
    return [super forwardingTargetForSelector:aSelector];
}

複製代碼

返回的Student對象,實際上就至關於調用了objc_msgSend([Student new], aSelector)code

若是上述方法沒有實現或者說沒有返回一個能夠處理消息的對象,那麼就會進入方法簽名,而後forwardInvocation階段。orm

這個階段也會視消息類型調用不一樣的實例方法和類方法: 實例方法:- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector- (void)forwardInvocation:(NSInvocation *)anInvocation;類方法:+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector+ (void)forwardInvocation:(NSInvocation *)anInvocation對象

methodSignatureForSelector方法返回一個方法簽名,方法簽名就是按照編碼規則生成的一個字符串的簽名實例。編碼規則在Runtime簡析中已經介紹過,其實就是用method_t結構中的types初始化的實例,它表明了方法的結構,好比返回值類型,參數類型等。

通過methodSignatureForSelector簽名以後就能夠Invocation,提供一個能夠調用該方法的實施做爲target,返回。

// 實例方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
   if (aSelector == @selector(test)) {
       return [NSMethodSignature signatureWithObjCTypes:"v@:"];
   }
   return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
   [anInvocation invokeWithTarget:[[Student alloc] init]];
}

// 類方法
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
   if (aSelector == @selector(test)) {
       return [NSMethodSignature signatureWithObjCTypes:"v@:"];
   }
   return [super methodSignatureForSelector:aSelector];
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {
   [anInvocation invokeWithTarget:[[Student alloc] init]];
}
複製代碼

END

ObJC中的方法調用的本質是消息機制,經過objc_msgSend,通過三個階段:方法查找,動態解析,和消息轉發階段,若是這幾個階段都沒有提供解決方案,那麼就會拋出經典的unrecognized selector sent to class錯誤。

相關文章
相關標籤/搜索