Runtime之objc_msgSend執(zhí)行流程

總覽

Objetive-C的消息發(fā)送,是通過(guò)objc_msgSend來(lái)實(shí)現(xiàn)的,具體執(zhí)行過(guò)程,主要分三個(gè)階段:

  • 1、消息發(fā)送;
  • 2、動(dòng)態(tài)方法解析
  • 3、消息轉(zhuǎn)發(fā)或重新簽名

消息發(fā)送

Person類(lèi)有兩個(gè)方法 sayHellosayBye ,Student繼承Person,并重寫(xiě) sayHello 方法。

@interface Person : NSObject
-(void)sayHello;
-(void)sayBye;
@end

@interface Student : Person
@end
    
@implementation Person
-(void)sayHello{
    NSLog(@"%s",__func__);
}
-(void)sayBye{
    NSLog(@"%s",__func__);
}
@end
    
@implementation Student
-(void)sayHello{
    NSLog(@"%s",__func__);
}
@end

現(xiàn)在,通過(guò)給Student實(shí)例對(duì)象發(fā)消息,來(lái)展示消息的調(diào)用順序

    //1.1 如果接收者類(lèi)的cache中能找到方法,則直接調(diào)用。
    //否則從接受者類(lèi)的方法列表中查找方法,找到后添加到cache中
    Student* student = [[Student alloc] init];
    [student sayHello];
    //1.2 以上兩個(gè)步驟均找不到的時(shí)候,從superClass的cache中查找,同 1.1
    [student sayBye];

結(jié)果如下:

2019-01-23 19:09:00.825000+0800 runtime_objc_msgSend[14364:5870808] -[Student sayHello]
2019-01-23 19:09:00.825100+0800 runtime_objc_msgSend[14364:5870808] -[Person sayBye]

通過(guò)對(duì)結(jié)果的分析,我們得到如下方法查找的順序:

  • 1 接收者首先從接收者類(lèi)的cache中查找方法
    • 1.1 如果能找到方法,直接調(diào)用,結(jié)束
    • 1.2 如果找不到方法,繼續(xù)執(zhí)行2
  • 2 從接收者類(lèi)的方法列表中查找
    • 2.1 如果找到方法,調(diào)用并將方法添加到接收者類(lèi)的cache中,結(jié)束
    • 2.2 如果找不到方法,則從其superClass的cache中查找
    • 遞歸2.1,直到最頂層類(lèi)。
  • 3 如果找不到方法,則判斷走以下兩個(gè)步驟
    • 3.1 如果兩個(gè)步驟均不涉及,則直接拋出異常 'unrecognized selector sent to instance'
    • 詳細(xì)步驟參照以下階段
      • 注:每個(gè)階段結(jié)束會(huì)重新進(jìn)入本階段。

動(dòng)態(tài)方法解析

如果消息發(fā)送階段,未找到匹配的方法,則開(kāi)發(fā)者可以通過(guò)重寫(xiě)NSObject中的以下兩個(gè)方法來(lái)對(duì)未匹配的方法進(jìn)行解析。

+ (BOOL)resolveClassMethod:(SEL)sel
+ (BOOL)resolveInstanceMethod:(SEL)sel

為了測(cè)試代碼,我們重寫(xiě)Student類(lèi),對(duì)其進(jìn)行擴(kuò)展,當(dāng)方法dynamicAnalysisMethod不存在時(shí),我們將Student類(lèi)中方法dynamicAnalysisOther的實(shí)現(xiàn)添加給dynamicAnalysisMethod。代碼如下

@interface Student : Person
-(void)dynamicAnalysisMethod;
@end
    
@implementation Student
+(BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(dynamicAnalysisMethod)) {
        Method method = class_getInstanceMethod([self class], @selector(dynamicAnalysisOther));
        //Adds a new method to a class with a given name and implementation.
        class_addMethod([self class],
                        sel,
                        method_getImplementation(method),
                        method_getTypeEncoding(method));
        return true;
    }
    return [super resolveInstanceMethod:sel];
}

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

此時(shí),我們給Student實(shí)例對(duì)象發(fā)dynamicAnalysisMethod消息,代碼如下

    Student* student = [[Student alloc] init];
    //針對(duì)類(lèi)和實(shí)例對(duì)象方法。
    //2.1重寫(xiě)NSObject的方法 + (BOOL)resolveClassMethod:(SEL)sel 
    //      或 + (BOOL)resolveInstanceMethod:(SEL)sel
    //2.2在方法中對(duì)方法進(jìn)行動(dòng)態(tài)解析。
    [student dynamicAnalysisMethod];

結(jié)果如下:

2019-01-23 19:09:00.825300+0800 runtime_objc_msgSend[14364:5870808] -[Student dynamicAnalysisOther]

這樣,我們實(shí)現(xiàn)了消息的動(dòng)態(tài)解析。

  • 針對(duì)未匹配的方法,我們可以通過(guò) class_addMethod 給類(lèi)添加新的方法和實(shí)現(xiàn)
  • 重新進(jìn)入消息發(fā)送階段

消息轉(zhuǎn)發(fā)

如果在以上兩個(gè)階段均沒(méi)有找到相關(guān)方法,此時(shí)就進(jìn)入了消息轉(zhuǎn)發(fā)階段。消息轉(zhuǎn)發(fā)主要有兩個(gè)類(lèi)別

  • 直接轉(zhuǎn)發(fā)
  • 方法重簽名,轉(zhuǎn)發(fā)

此時(shí),我們新建一個(gè)Worker類(lèi),詳細(xì)代碼如下:

@interface Worker : NSObject
-(void)sayHello;
-(void)reSignature;
@end
    
@implementation Worker
-(id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(sayHello)) {
        return [[Student alloc] init];
    }
    return nil;
}

-(NSMethodSignature*)methodSignatureForSelector:(SEL)sel{
    if (sel == @selector(reSignature)) {
       NSMethodSignature* signature = [[[Student alloc]
                                         init]
                                        methodSignatureForSelector:@selector(reSignatureMethod)];
        return signature;
    }
    return [super methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
    NSLog(@"%s",__func__);
    if (anInvocation.selector == @selector(reSignatureMethod)) {
        anInvocation.target = [[Student alloc] init];
        [anInvocation invoke];
        //或者如下形式
        //[anInvocation invokeWithTarget:[[Student alloc] init]];
    }
}
@end

對(duì)Worker類(lèi),有兩個(gè)方法申明sayHelloreSignature , 但是并不對(duì)其進(jìn)行實(shí)現(xiàn)。此時(shí)我們給Worker的實(shí)例對(duì)象發(fā)送消息,如下:

    Worker* worker = [[Worker alloc] init];
    //直接轉(zhuǎn)發(fā)
    //3.1 重寫(xiě)NSObject的方法 - (id)forwardingTargetForSelector:(SEL)aSelector
    //返回 消息接收者對(duì)象
    [worker sayHello];
    
    //方法重簽名。
    //如果3.1轉(zhuǎn)發(fā)方法返回的是nil。則可以通過(guò)重新簽名的方式來(lái)實(shí)現(xiàn)
    //4.1 重寫(xiě)NSObject的類(lèi)/實(shí)例方法 
    //    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    //      或 + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector
    //4.2 在方法中,返回新方法的方法簽名
    //4.3 重寫(xiě)NSObject的方法 - (void)forwardInvocation:(NSInvocation *)anInvocation
    //根據(jù)簽名等信息,對(duì)NSInvocation的target進(jìn)行賦值。然后invoke喚醒
    [worker reSignature];

方法均沒(méi)有實(shí)現(xiàn),我們通過(guò)消息轉(zhuǎn)發(fā),結(jié)果如下:

2019-01-23 19:09:00.825449+0800 runtime_objc_msgSend[14364:5870808] -[Student sayHello]
2019-01-23 19:09:00.825554+0800 runtime_objc_msgSend[14364:5870808] -[Worker forwardInvocation:]

消息直接轉(zhuǎn)發(fā)

  • 重寫(xiě)NSObject的 -(id)forwardingTargetForSelector:(SEL)aSelector方法
  • 直接返回接收消息的對(duì)象實(shí)例。

方法重新簽名

  • 通過(guò)重寫(xiě)NSObject的方法

    • - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 
      + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector 
      
    • 在方法中,我們針對(duì)reSignature selector進(jìn)行了重新簽名

  • 重寫(xiě)NSObject方法

    • - (void)forwardInvocation:(NSInvocation *)anInvocation 
      
    • 方法中,對(duì) reSignatureMethod selector的target進(jìn)行了重新賦值

    • 喚醒

  • 進(jìn)入方法發(fā)送階段

Demo

https://github.com/liangtongdev/Demo-runtime_objc_msgSend

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,032評(píng)論 0 9
  • 文中的實(shí)驗(yàn)代碼我放在了這個(gè)項(xiàng)目中。 以下內(nèi)容是我通過(guò)整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 1,021評(píng)論 0 6
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 827評(píng)論 0 2
  • 本文詳細(xì)整理了 Cocoa 的 Runtime 系統(tǒng)的知識(shí),它使得 Objective-C 如虎添翼,具備了靈活的...
    lylaut閱讀 864評(píng)論 0 4
  • Objective-C中有兩個(gè)NSObject,一個(gè)是NSObject類(lèi),另一個(gè)是NSObject協(xié)議。而其中NS...
    ScaryMonsterLyn閱讀 796評(píng)論 0 2

友情鏈接更多精彩內(nèi)容