Objective-C Runtime的消息機制以及消息轉(zhuǎn)發(fā)機制

OC Runtime

OC Runtime 是一個 Runtime 庫喳挑,主要以 C 和匯編語言為基礎(chǔ)味赃,使用面向?qū)ο蟮?OC 來編寫掀抹。可以加載類心俗,對消息進行轉(zhuǎn)發(fā)傲武、分發(fā)等。
RunTime 庫是 Objective-C 面向?qū)ο蠛蛣討B(tài)機制的基石城榛。
這句話已經(jīng)充分說明了 RunTime 的重要性揪利。

消息傳遞

我們都知道,在編譯時 Objective-C 函數(shù)調(diào)用的語法都會被翻譯成 C 的函數(shù)調(diào)用 objc_msgSend()吠谢,如下事例:

[obj doSomethingWithIndex:10];

等價于:

objc_msgSend(obj, @selector(doSomethingWithIndex:), 10);

重點在于 objc_object 中的 isa 指針和 objc_class 中的分發(fā)表 class dispatch table

objc_object 相關(guān)定義

struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

objc_class 相關(guān)定義

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

objc_method 相關(guān)定義

struct objc_method {
    SEL _Nonnull method_name                                 OBJC2_UNAVAILABLE;
    char * _Nullable method_types                            OBJC2_UNAVAILABLE;
    IMP _Nonnull method_imp                                  OBJC2_UNAVAILABLE;
};

struct objc_method_list {
    struct objc_method_list * _Nullable obsolete             OBJC2_UNAVAILABLE;

    int method_count                                         OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]                        OBJC2_UNAVAILABLE;
};

objc_method_list 是什么土童?
其本質(zhì)是一個有 objc_method 元素的可變長度的數(shù)組。

objc_method 結(jié)構(gòu)體中都有些什么工坊?
SEL _Nonnull method_name : 函數(shù)名
char * _Nullable method_types : 函數(shù)類型的字符串
IMP _Nonnull method_imp : 函數(shù)的實現(xiàn)

通過以上献汗,不難看出,在 Objective-C 中王污,對象罢吃、類、方法都是一個 C 的結(jié)構(gòu)體昭齐。

到這里應(yīng)該能大致猜出 objc_msgSend() 到底做了哪些工作尿招。

例:objc_msgSend(obj, @selector(doSomethingWithIndex:), 10);

  1. 通過 objisa 指針找到它的 class;
  2. classmethodLists 中找 doSomethingWithIndex:;
  3. class 中未找到 doSomethingWithIndex: ,前往 super_class 中找;
  4. 一旦找到 doSomethingWithIndex: ,立即執(zhí)行 method_imp就谜。

是不是每個消息都要按以上步驟把 objc_method_list 遍歷一遍怪蔑?
注意我們的 objc_class 中有一個成員 objc_cache,那么它又是做什么的呢丧荐?

事實上缆瓣,在找到 doSomethingWithIndex: 方法后,將該方法的 method_name 作為 key 虹统,method_imp 作為 value 存起來弓坞。當(dāng)下次再收到 doSomethingWithIndex: 消息時,直接去 cache 里拿到车荔,并執(zhí)行 method_imp渡冻。

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

平時開發(fā)中,我們都曾遇到過以下的錯誤提示:

  1. unrecognized selector sent to instance
  2. unrecognized selector sent to class

不用怕忧便, RunTime 會提供給我們?nèi)握鹊臋C會族吻。

第一次機會:

+ (BOOL)resolveInstanceMethod:(SEL)sel

+ (BOOL)resolveClassMethod:(SEL)sel

在這里,我們可以新添加一個函數(shù)實現(xiàn)并返回 YES 茬腿,運行時系統(tǒng)會重新啟動一次消息發(fā)送的過程呼奢。如下:

void addNewMethod(id self, SEL _cmd, NSString *str) {
    NSLog(@"新添加的方法------%@", str);
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {   
    if (sel == @selector(doSomeThing:)) {
        class_addMethod(self, sel, (IMP)runTimeMethod, "v@:@");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

注意:v@:@ 具體什么含義,可查閱 Type Encodings 官方文檔

如果第一次機會中的方法返回為 NO 切平,此時會進行第二次機會握础。

第二次機會:

- (id)forwardingTargetForSelector:(SEL)aSelector

將對應(yīng)消息轉(zhuǎn)發(fā)給其他對象,只要返回的不是 nilself 悴品,發(fā)送消息的過程會被重啟禀综,此時發(fā)送的對象變?yōu)榉祷氐哪莻€對象。如下:

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if (sel == @selector(doSomeThing:)) {
        return [[NewClass alloc] init];
    }
}

第三次機會:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
獲得函數(shù)的參數(shù)和返回值類型

若返回為 nil 苔严,會發(fā)出
- (void)doesNotRecognizeSelector:(SEL)aSelector;
消息定枷,同時程序會掛掉

若返回函數(shù)簽名,RunTime 會創(chuàng)建 NSInvocation 對象届氢,并發(fā)送
- (void)forwardInvocation:(NSInvocation *)anInvocation
給目標(biāo)對象欠窒,如下:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    if (aSelector == @selector(customMethod)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }else {
        return [super methodSignatureForSelector:aSelector];
    }
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{  
    SEL another_selector = [anInvocation selector];

    if ([another_object respondsToSelector:another_selector]) {
        [anInvocation invokeWithTarget:[[NewClass alloc] init]];
    }else {
        [super forwardInvocation:anInvocation];
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市退子,隨后出現(xiàn)的幾起案子岖妄,更是在濱河造成了極大的恐慌,老刑警劉巖寂祥,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荐虐,死亡現(xiàn)場離奇詭異,居然都是意外死亡丸凭,警方通過查閱死者的電腦和手機福扬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門腕铸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人铛碑,你說我怎么就攤上這事狠裹。” “怎么了汽烦?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵酪耳,是天一觀的道長。 經(jīng)常有香客問我刹缝,道長,這世上最難降的妖魔是什么颈将? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任梢夯,我火速辦了婚禮,結(jié)果婚禮上晴圾,老公的妹妹穿的比我還像新娘颂砸。我一直安慰自己,他們只是感情好死姚,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布人乓。 她就那樣靜靜地躺著,像睡著了一般都毒。 火紅的嫁衣襯著肌膚如雪色罚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天账劲,我揣著相機與錄音戳护,去河邊找鬼。 笑死瀑焦,一個胖子當(dāng)著我的面吹牛腌且,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播榛瓮,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼铺董,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了禀晓?” 一聲冷哼從身側(cè)響起精续,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎匆绣,沒想到半個月后驻右,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡崎淳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年堪夭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡森爽,死狀恐怖恨豁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情爬迟,我是刑警寧澤橘蜜,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站付呕,受9級特大地震影響计福,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜徽职,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一象颖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧姆钉,春花似錦说订、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至毯辅,卻和暖如春埂伦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背思恐。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工赤屋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人壁袄。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓类早,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嗜逻。 傳聞我的和親對象是個殘疾皇子涩僻,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

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