OC方法調(diào)用過程

OC調(diào)用方法的機(jī)制其實就是消息轉(zhuǎn)發(fā)機(jī)制,最終都是轉(zhuǎn)成objc_msgSend的函數(shù)調(diào)用赔退。接下來我們就一起
我們先來看下類的底層結(jié)構(gòu)![

class底層結(jié)構(gòu)

消息發(fā)送

第一步: 查詢接收者是否為空,如果為空钢颂,直接退出经窖,如果不為空走第二步
第二步: 查詢緩存cash中是否有該方法,如果有則調(diào)用方法,如果沒有走第三步彭沼。值的注意的是緩存中存儲方法使用散列表列表的方式存儲的缔逛。
第三步: 查詢class_rw_tmethods中是否有該方法,如果有則調(diào)用方法,并且添加到緩存中姓惑,如果沒有走第四步褐奴。值的注意的是,這個查詢方法是根據(jù)該方法列表是否已經(jīng)排好序了于毙,如果已經(jīng)排序則用二分法查找敦冬,如果沒有則是遍歷查找
第四步: 通過superclass查詢父類否有該方法,如果有則調(diào)用方法,并且添加到自己的緩存中唯沮,如果沒有繼續(xù)調(diào)用第四步脖旱,如果查找到基類都沒有這個方法,那么就執(zhí)行第二階段動態(tài)解析介蛉。值的注意的是萌庆,這一步的查找方式是執(zhí)行的父類中第二步跟第三步。

動態(tài)解析

一旦消息發(fā)送階段沒有找到方法币旧,那么就會執(zhí)行動態(tài)解析階段践险,會調(diào)用兩個方法
對象方法 調(diào)用 + (BOOL)resolveInstanceMethod:(SEL)sel
類方法 調(diào)用+ (BOOL)resolveClassMethod:(SEL)sel
這兩個方法基本一致,這里我們介紹一下對象方法

.h
@interface DDPerson : NSObject
- (void)eat;
@end

.m
#import "DDPerson.h"
#import <objc/runtime.h>
@implementation DDPerson

- (void)test{
    NSLog(@"調(diào)用了test方法");
}

// 這個是動態(tài)添加對象方法
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(eat)) {
        // 獲取要動態(tài)添加的方法
        Method mothod = class_getInstanceMethod(self, @selector(test));
        // 獲取添加方法的Imp也就是方法地址
        IMP imp = method_getImplementation(mothod);
        // 獲取字符串編碼 typeEconding
        const char *types = method_getTypeEncoding(mothod);
        // 添加方法
        class_addMethod(self, sel, imp, types);
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
@end

main.m
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        DDPerson *person = [[DDPerson alloc] init];
        [person eat];
    }
    return 0;
}

代碼分析
.h文件中我們聲明了eat對象方法吹菱,但是在.m文件中我們沒有實現(xiàn)巍虫,我們只加入了test方法。當(dāng)程序自動調(diào)用resolveInstanceMethod時,我們可以通過調(diào)用class_addMethod像對象添加一個動態(tài)方法鳍刷,也就是說真正要調(diào)用的test方法占遥。class_addMethod參數(shù)介紹:
cls :第一個參數(shù),給誰發(fā)送消息输瓜。如果是對象方法瓦胎,那么這里傳入接收者的類對象,如果是類方法前痘,那么這里傳入的是接收者的元類對象凛捏。
SEL :第二個參數(shù),動態(tài)添加的方法名稱
IMP :第三個參數(shù)芹缔,動態(tài)添加的方法地址
types :第四個參數(shù)坯癣,動態(tài)添加的方法的字符串編碼

當(dāng)調(diào)用eat方法時的執(zhí)行結(jié)果:

2021-05-26 09:29:30.976106+0800 消息轉(zhuǎn)發(fā)[19393:703271] 調(diào)用了test方法
Program ended with exit code: 0

?????? 1. 這里動態(tài)添加方法是添加到了類或者是元類中的class_rw_t中的methods方法列表中,而沒有存到方法的緩存中最欠。添加之后會重新走消息發(fā)送階段
2. 動態(tài)解析階段示罗,允許用戶調(diào)用其他類的方法惩猫,我們只需把消息接收者改變?yōu)閷?yīng)類就行了,比如我們想要調(diào)用Student的study方法蚜点,我們只要把消息接收這改為Student方法改為study就可以了Method mothod = class_getInstanceMethod([DDStudent class], @selector(study));
3. 動態(tài)解析過后轧房,程序把這個方法標(biāo)記為已經(jīng)動態(tài)解析,之后又會重新走第一個階段(消息發(fā)送)

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

如果沒有實現(xiàn)第二階段的任何方法绍绘,也就是沒有添加任何新方法奶镶,那么方法調(diào)用來到第三階段,消息轉(zhuǎn)發(fā)階段陪拘。
第一步: 調(diào)用 forwardingTargetForSelector

- (id)forwardingTargetForSelector:(SEL)aSelector {
    // 這里返回的是一個對象厂镇,就是想要把消息轉(zhuǎn)發(fā)給的對象
    // 比如有一個對象Student中 也有一個同樣的調(diào)用方法 那么我們這里就可以直接返回這個對象就可以了
    if (aSelector == @selector(eat)) {
        return [[DDStudent alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}

?????? 返回對象中必須有一個跟你調(diào)用方法一樣的方法,才能返回這個對象否則程序會調(diào)用doesNotRecognizeSelector方法左刽,終止程序運行捺信,并且提示unrecognized selector sent to instance 0x100615cc0' 這個常見提示

第二步: 調(diào)用 methodSignatureForSelector
如果在第一步?jīng)]有調(diào)用forwardingTargetForSelector或者返回值是nil那么就執(zhí)行第二步

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(eat)) {
        // 創(chuàng)建NSMethodSignature實例
        NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:i"];
        return signatrure;
    }
    return [super methodSignatureForSelector:aSelector];
}

這里需要返回一個方法的簽名,方法簽名需要傳遞一個char類型字符串欠痴,
字符串中第一個字符是返回值v代表void迄靠,接下來依次是方法傳遞的參數(shù)@表示對象,:代表地址喇辽,i代表int類型

這三步: 如果執(zhí)行了第二步就必須實現(xiàn) forwardInvocation掌挚,并且最終執(zhí)行也是在這個方法中,NSInvocation這個對象中包含方法調(diào)用者 方法名 方法參數(shù)

// anInvocation 包含方法調(diào)用者 方法名 方法參數(shù)
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"喲呵呵呵 呦呵喲呵");
    // 切換方法調(diào)用者
    [anInvocation invokeWithTarget:[[DDStudent alloc] init]];
    // 獲取指定參數(shù) 注意這里
    int age;
    [anInvocation getArgument:&age atIndex:2];
}

獲取參數(shù):調(diào)用獲取參數(shù)方法的是后茵臭,索引值就是方法傳遞參數(shù)的索引疫诽,由于每一個方法都包含兩個默認(rèn)參數(shù) id類型 還有一個SEL方法名舅世,所以我們自己傳入的參數(shù)索引只是從2開始的旦委。還有一點需要注意,索引值應(yīng)該小于參數(shù)個數(shù)雏亚,否則會報數(shù)組越界錯誤缨硝。

拋出異常

如果上述三個階段都沒有找到調(diào)用方法那么程序執(zhí)行doesNotRecognizeSelector方法,終止程序運行罢低,并且提示unrecognized selector sent to instance 0x100615cc0' 這個常見提示
點擊這里查看總結(jié)查辩。 大家加油!M帧宜岛!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市功舀,隨后出現(xiàn)的幾起案子萍倡,更是在濱河造成了極大的恐慌,老刑警劉巖辟汰,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件列敲,死亡現(xiàn)場離奇詭異阱佛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)戴而,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門凑术,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人所意,你說我怎么就攤上這事淮逊。” “怎么了扶踊?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵壮莹,是天一觀的道長。 經(jīng)常有香客問我姻檀,道長命满,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任绣版,我火速辦了婚禮胶台,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘杂抽。我一直安慰自己诈唬,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布缩麸。 她就那樣靜靜地躺著铸磅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪杭朱。 梳的紋絲不亂的頭發(fā)上阅仔,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機(jī)與錄音弧械,去河邊找鬼八酒。 笑死,一個胖子當(dāng)著我的面吹牛刃唐,可吹牛的內(nèi)容都是我干的羞迷。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼画饥,長吁一口氣:“原來是場噩夢啊……” “哼衔瓮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起抖甘,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤热鞍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碍现,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡幅疼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了昼接。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爽篷。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖慢睡,靈堂內(nèi)的尸體忽然破棺而出逐工,到底是詐尸還是另有隱情,我是刑警寧澤漂辐,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布泪喊,位于F島的核電站,受9級特大地震影響髓涯,放射性物質(zhì)發(fā)生泄漏袒啼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一纬纪、第九天 我趴在偏房一處隱蔽的房頂上張望蚓再。 院中可真熱鬧,春花似錦包各、人聲如沸摘仅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽娃属。三九已至,卻和暖如春护姆,著一層夾襖步出監(jiān)牢的瞬間矾端,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工签则, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留须床,地道東北人铐料。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓渐裂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親钠惩。 傳聞我的和親對象是個殘疾皇子柒凉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容