方法動態(tài)解析

在OC中方法的調(diào)用被稱為消息的發(fā)送.例如: [obj test] 這段代碼會裝換為objc_msgSend(obj, test)這段運行時代碼;

而關于objc_msgSend(obj, test)這段代碼的掉用過程如下:

步驟一:

  • 當調(diào)用對象方法時,首先回去當前對象的isa指針指向的類method_list中找方法的實現(xiàn),如果沒有找到,則會去父類的method_list中尋找,沒有則繼續(xù)往父類尋找,直至到root class為止。

  • 當調(diào)用類方法時,首先會去當前類的isa指針所指向的meta class的method_list中找方法的實現(xiàn),如果沒有找到,則會去父類的meta class的method_list中尋找,沒有則繼續(xù)往父類的meta class尋找,直至root meta class.

步驟二:

如果在步驟一中沒有找到方法的實現(xiàn),則會進入方法的動態(tài)解析過程.

方法動態(tài)解析

方法的動態(tài)解析使程序有三次機會避免出現(xiàn)找不到方法實現(xiàn)的crash機會。

過程如圖(圖片來源網(wǎng)絡):

method.png

第一步(動態(tài)添加方法的實現(xiàn))

通過resolveInstanceMethod:方法,(類方法用resolveClassMethod:),返回Yes,可以通過class_addMethod動態(tài)添加方法實現(xiàn).如果返回No,則進入下一步.而如果返回Yes,但沒有添加方法的實現(xiàn),則程序就會在這一步直接crash掉了。

例子

@interface Person : NSObject

- (void)run;

@end

#import "Person.h"
#import <objc/runtime.h>

@implementation Person

+ (BOOL)resolveInstanceMethod:(SEL)sel {

    if ([NSStringFromSelector(sel) isEqualToString:@"run"]) {
        
        
        class_addMethod(self, sel, addRun, "v@:");
        
        return YES;
    }
    
    return [super resolveInstanceMethod:sel];
}

void addRun() {

    NSLog(@"addRun");
}



@end


最終程序正常運行,打印結(jié)果為addRun.本例中person類只有run這個實例方法的聲明,而沒有實現(xiàn),當外部直接調(diào)用這個方法時,會因為找不到run方法的實現(xiàn)而crash.而通過實現(xiàn) resolveInstanceMethod方法返回YES,直接動態(tài)添加方法的實現(xiàn)addRun,使得程序在調(diào)用run方法時調(diào)用到addRun這個實現(xiàn).

第二步 (將消息轉(zhuǎn)發(fā)給其它對象處理)

到這一步,可以通過forwardingTargetForSelector方法講這個消息轉(zhuǎn)發(fā)給其它對象處理,當返回nil時,將進入第三步;當返回self,則會直接因為找不到方法實現(xiàn)而直接crash;當返回其它對象時,則將這個方法轉(zhuǎn)發(fā)給其它類去實現(xiàn).

例子

@interface Person : NSObject

- (void)walk;

@end

#import "Person.h"
#import <objc/runtime.h>
#import "Animal.h"

@implementation Person


- (id)forwardingTargetForSelector:(SEL)aSelector {
    if ([NSStringFromSelector(aSelector) isEqualToString:@"walk"]) {


        return [[Animal alloc]init];
    }

    return [super forwardingTargetForSelector:aSelector];
}


@end

#import "Animal.h"

@implementation Animal

- (void)walk {

    NSLog(@"animal walk");

}

@end

程序正常運行,打印animal walk.在本例中, Person類聲明了一個實例方法而沒有實現(xiàn),并且沒有動態(tài)添加方法的實現(xiàn),而是將walk方法轉(zhuǎn)發(fā)給Animal這個類實現(xiàn),所以最后調(diào)用的是animal的walk方法的實現(xiàn)。

第三步 (修改方法的實現(xiàn)和方法的響應對象)

到了最后一步,可以通過methodSignatureForSelector,返回一個NSMethodSignature,來修改方法的實現(xiàn)和方法響應對象.如果返回nil,則直接crash掉了。

例子

@interface Person : NSObject

- (void)jump;

@end


#import "Person.h"
#import <objc/runtime.h>
#import "Animal.h"

@implementation Person


- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
        if ([NSStringFromSelector(aSelector) isEqualToString:@"jump"]) {
    
    
            return [NSMethodSignature signatureWithObjCTypes:"v@:"];
        }
    
        return [super methodSignatureForSelector:aSelector];


}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
 
    [anInvocation setSelector:@selector(changeJump)];
    [anInvocation invokeWithTarget:self];

}

- (void)changeJump {

    NSLog(@"changeJump");
    
}

@end

程序正常運行,并打印changeJump,本例中Person類的jump方法只有聲明而沒有方法的實現(xiàn),并且沒有動態(tài)添加方法的實現(xiàn)和轉(zhuǎn)發(fā)給其它對象,而是修改了方法的實現(xiàn),將jump方法的實現(xiàn)改為了changeJump方法的實現(xiàn).

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

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,041評論 0 9
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,872評論 25 709
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,881評論 33 466
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,533評論 19 139
  • 貴有恒,何必三更起,五更睡; 最無益,只怕一日曝,十日寒。 據(jù)史料記載,這副對聯(lián)是毛澤東主席,1914年在湖南一師...
    亮靚_27d5閱讀 539評論 29 44

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