iOS:Runtime - objc_msgSend函數(shù)

筆記記錄:來源于apple的文檔,具體參考:apple文檔

消息傳遞

本章介紹如何將消息表達(dá)式轉(zhuǎn)換objc_msgSend函數(shù)調(diào)用,以及如何按名稱引用方法。然后,它說明了如何利用objc_msgSend,以及(如果需要)如何規(guī)避動(dòng)態(tài)綁定。

objc_msgSend函數(shù)

在Objective-C中,消息直到運(yùn)行時(shí)才綁定到方法實(shí)現(xiàn)。編譯器轉(zhuǎn)換一個(gè)消息表達(dá)式,

[receiver message]

調(diào)用消息傳遞功能, objc_msgSend。此功能需要接收器 消息中提到的方法的名稱(即方法選擇器)作為其兩個(gè)主要參數(shù):

objc_msgSend(receiver, selector)

所有的屬性也都是通過objc_msgSend進(jìn)行轉(zhuǎn)發(fā)的:

objc_msgSend(receiver, selector, arg1, arg2, ...)

消息傳遞功能完成了動(dòng)態(tài)綁定所需的一切:

  • 它首先找到選擇器的過程(方法實(shí)現(xiàn))指。由于相同的方法可以通過不同的類不同地實(shí)現(xiàn),因此它找到的精確過程取決于接收者的類。
  • 然后,它將調(diào)用該過程,并將接收對(duì)象(指向其數(shù)據(jù)的指針)以及為該方法指定的所有參數(shù)傳遞給該過程。
  • 最后,它將過程的返回值作為自己的返回值傳遞。

注意: 編譯器會(huì)生成對(duì)消息傳遞功能的調(diào)用。您永遠(yuǎn)不需要在編寫的代碼中直接調(diào)用它。

消息傳遞的關(guān)鍵在于編譯器為每個(gè)類和對(duì)象構(gòu)建的結(jié)構(gòu)。每個(gè)類結(jié)構(gòu)都包含以下兩個(gè)基本元素:

  • 指向超類的指針。

  • 調(diào)度表。該表具有將方法選擇器與其所標(biāo)識(shí)的方法的類特定地址相關(guān)聯(lián)的條目。setOrigin::方法的選擇器與(實(shí)現(xiàn)的過程)的地址相關(guān)聯(lián),方法setOrigin::的選擇器displaydisplay的地址相關(guān)聯(lián),依此類推。
    創(chuàng)建新對(duì)象時(shí),將為其分配內(nèi)存,并初始化其實(shí)例變量。對(duì)象變量中的第一個(gè)是指向其類結(jié)構(gòu)的指針。該指針稱為isa,它使對(duì)象可以訪問其類,并可以通過該類訪問其繼承的所有類。
    **注意:** 雖然嚴(yán)格來說,語言不是語言的一部分,但isa對(duì)象與Objective-C運(yùn)行時(shí)系統(tǒng)一起使用時(shí)需要使用指針。一個(gè)對(duì)象必須與一個(gè)對(duì)象“等效”。struct objc_object(在objc/objc.h中定義)在結(jié)構(gòu)定義的任何字段中。但是,很少(如果有的話)需要?jiǎng)?chuàng)建自己的根對(duì)象,并且從該變量繼承NSObjectNSProxy自動(dòng)具有該isa變量的對(duì)象。

這些類和對(duì)象結(jié)構(gòu)的元素如圖3-1所示。
圖3-1 消息傳遞框架


image.png

當(dāng)消息發(fā)送到對(duì)象時(shí),消息傳遞功能將跟隨對(duì)象的 isa指向類結(jié)構(gòu)的指針,該類結(jié)構(gòu)在調(diào)度表中查找方法選擇器。如果無法在其中找到選擇器,則objc_msgSend跟隨指向超類的指針,并嘗試在其調(diào)度表中找到選擇器。連續(xù)的失敗導(dǎo)致objc_msgSend爬升類層次結(jié)構(gòu),直到到達(dá)NSObject類。找到選擇器后,該函數(shù)將調(diào)用在表中輸入的方法,并將該方法傳遞給接收對(duì)象的數(shù)據(jù)結(jié)構(gòu)。

這是在運(yùn)行時(shí)選擇方法實(shí)現(xiàn)的方式,或者,在面向?qū)ο缶幊痰男g(shù)語中,方法是動(dòng)態(tài)綁定到消息的。

為了加快消息傳遞過程,運(yùn)行時(shí)系統(tǒng)會(huì)在使用方法的選擇器和地址時(shí)對(duì)其進(jìn)行緩存。每個(gè)類都有一個(gè)單獨(dú)的緩存,并且可以包含繼承的方法以及該類中定義的方法的選擇器。在搜索調(diào)度表之前,消息傳遞例程首先檢查接收對(duì)象的類的緩存(根據(jù)理論,曾經(jīng)使用過的方法可能會(huì)再次使用)。如果方法選擇器在緩存中,則消息傳遞僅比函數(shù)調(diào)用慢一點(diǎn)。一旦程序運(yùn)行了足夠長的時(shí)間以“預(yù)熱”其緩存,幾乎它發(fā)送的所有消息都將找到一個(gè)緩存方法。緩存在程序運(yùn)行時(shí)動(dòng)態(tài)增長以容納新消息。

使用隱藏參數(shù)

當(dāng)objc_msgSend找到實(shí)現(xiàn)方法的過程時(shí),它將調(diào)用該過程并將消息中的所有參數(shù)傳遞給該過程。它還向過程傳遞兩個(gè)隱藏參數(shù):

  • 接收對(duì)象
  • 選擇器 對(duì)于方法

這些參數(shù)為每個(gè)方法實(shí)現(xiàn)提供了有關(guān)調(diào)用它的消息表達(dá)式的兩半的明確信息。之所以說它們是“隱藏的”,是因?yàn)樗鼈儧]有在定義該方法的源代碼中聲明。在編譯代碼時(shí)將它們插入到實(shí)現(xiàn)中。
盡管未明確聲明這些參數(shù),但是源代碼仍然可以引用它們(就像可以引用接收對(duì)象的實(shí)例變量一樣)。方法將接收對(duì)象稱為self,并將其作為自己的選擇器 _cmd。在下面的示例中,_cmd引用strange方法的選擇器和self接收strange消息的對(duì)象

- (void)strange {
    id target = getTheReceiver();
    SEL method = getTheMethod();
    
    if ( target == self  || method == _cmd) {
        return nil;
    }
    return [target performSelector:method];
}

self是這兩個(gè)參數(shù)中更有用的。實(shí)際上,這是使接收對(duì)象的實(shí)例變量可用于方法定義的方式。

獲取方法地址

規(guī)避動(dòng)態(tài)綁定的唯一方法是獲取方法的地址并直接調(diào)用它,就好像它是一個(gè)函數(shù)一樣。在少數(shù)情況下,當(dāng)連續(xù)多次執(zhí)行特定方法,并且您希望避免每次執(zhí)行該方法時(shí)都要避免消息傳遞的開銷時(shí),這可能是合適的。

使用NSObject類中定義的方法,methodForSelector:,您可以要求一個(gè)指向?qū)崿F(xiàn)方法的過程的指針,然后使用該指針來調(diào)用該過程。methodForSelector:返回的指針必須仔細(xì)轉(zhuǎn)換為正確的函數(shù)類型。返回類型和參數(shù)類型都應(yīng)包含在強(qiáng)制類型轉(zhuǎn)換中。

下面的示例顯示了如何setFilled:調(diào)用實(shí)現(xiàn)該方法的過程:

void (*setter)(id, SEL, BOOL);
int i;
 
setter = (void (*)(id, SEL, BOOL))[target
    methodForSelector:@selector(setFilled:)];
for ( i = 0 ; i < 1000 ; i++ )
    setter(targetList[i], @selector(setFilled:), YES);

傳遞給過程的前兩個(gè)參數(shù)是接收對(duì)象(self)和方法選擇器(_cmd)。這些參數(shù)隱藏在方法語法中,但是在將方法作為函數(shù)調(diào)用時(shí)必須將其明確顯示。

使用methodForSelector:規(guī)避動(dòng)態(tài)綁定可以節(jié)省消息傳遞所需的大部分時(shí)間。但是,僅在重復(fù)多次重復(fù)一條特定消息的情況下,這種節(jié)省才是可觀的,如for上面所示的循環(huán)。

請注意,這methodForSelector:是由Cocoa運(yùn)行時(shí)系統(tǒng)提供的;它不是Objective-C語言本身的功能。

大千世界,求同存異;相遇是緣,相識(shí)是份,相知便是“猿糞”(緣分)
From MZou

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

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

  • 在OC中,如果像對(duì)象傳遞消息,就會(huì)調(diào)用動(dòng)態(tài)綁定機(jī)制來絕對(duì)調(diào)用的方法,對(duì)象在接收消息時(shí)調(diào)用的方法則由運(yùn)行期決定,也可...
    有一種再見叫青春閱讀 1,061評(píng)論 0 1
  • 關(guān)于runtime的運(yùn)用有:1 消息傳遞(調(diào)用方法): objc_msgSend2 動(dòng)態(tài)添加方法 : class_...
    神奇李白閱讀 385評(píng)論 0 3
  • OC,objc_msgSend() 簡介:用一句話簡介消息傳遞那就是:用一個(gè) C語言函數(shù),向一個(gè)實(shí)例傳遞一個(gè)字符串...
    BangRaJun閱讀 693評(píng)論 0 0
  • 前言 OC是一門動(dòng)態(tài)語言,就預(yù)示這光靠編譯過程來完全讀懂工作的方式是不夠的,很多執(zhí)行語句都需要在運(yùn)行時(shí)才能知道其意...
    涼秋落塵閱讀 210評(píng)論 0 0
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會(huì),身份的轉(zhuǎn)變要...
    余生動(dòng)聽閱讀 10,798評(píng)論 0 11

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