翻譯:Objective-C運(yùn)行時(shí)編程指南(三)——消息傳遞(Messaging)

本章描述了如何將消息表達(dá)式轉(zhuǎn)換成objc_msgSend函數(shù)調(diào)用,以及如何通過名字引用方法。然后解釋如何利用objc_msgSend以及如何避免動(dòng)態(tài)綁定

objc_msgSend函數(shù)

在Objective-C中,直到運(yùn)行時(shí),消息才會(huì)綁定到方法的實(shí)現(xiàn)。編譯器才會(huì)轉(zhuǎn)換消息表達(dá)式,
<pre><code>[receiver message]
</pre></code>

調(diào)用消息傳遞行數(shù)objc_msgSend。這個(gè)函數(shù)需要接收者和消息中提到的方法名即方法選擇器作為它的兩個(gè)主要參數(shù):
<pre><code>objc_msgSend(receiver, selector)
</pre></code>

消息中傳入的任何參數(shù)都可以在objc_msgSend處理:
<pre><code>objc_msgSend(receiver, selector, arg1, arg2, ...)
</pre></code>

消息傳遞函數(shù)支持動(dòng)態(tài)綁定:

  • 首先,獲取選擇器指向的程序(方法實(shí)現(xiàn))。因?yàn)橄嗤姆椒梢员徊煌念惙謩e實(shí)現(xiàn),獲取的具體程序取決于接收器的類。

  • 然后調(diào)用程序,通過傳遞接收對(duì)象(數(shù)據(jù)指針)以及方法中指定的任何參數(shù)。

  • 最后,它傳遞程序返回值作為自己的返回值。

注意:編譯器生成消息傳遞函數(shù)。不能在代碼中直接調(diào)用。

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

  • 指向父類的指針

  • Dispatch表類。這個(gè)表的記錄可以將方法選擇器與指定類方法的地址關(guān)聯(lián)。setOrigin:: 方法的選擇器與setOrigin::地址(程序?qū)崿F(xiàn))有關(guān),display 方法的選擇器與的display 地址有關(guān),等等

當(dāng)創(chuàng)建一個(gè)新對(duì)象,會(huì)分配內(nèi)存并初始化實(shí)例變量。首先,對(duì)象變量是一個(gè)指向類結(jié)構(gòu)的指針。該指針,稱為isa,通過類,對(duì)象可以訪問該類和該對(duì)象繼承的所有類。

注意:isa指針雖然不是語(yǔ)言嚴(yán)格意義上的一部分,但是是使用Objective-C運(yùn)行時(shí)系統(tǒng)所需的一個(gè)對(duì)象。一個(gè)對(duì)象須“等效于”結(jié)構(gòu)定義中的struct objc_object(定義于objc/objc.h)。然而,很少需要?jiǎng)?chuàng)建自己的根對(duì)象和繼承自NSObjectNSProxy 的對(duì)象,自動(dòng)有isa變量。

類元素和對(duì)象結(jié)構(gòu)如圖3-1所示。

圖3-1 消息傳遞框架

當(dāng)一個(gè)消息發(fā)送到一個(gè)對(duì)象,消息傳遞函數(shù)遵循對(duì)象的isa指針,該指針指向類結(jié)構(gòu),并在dispatch表中查找方法選擇器。如果不能找到選擇器,objc_msgSend則遵循指向父類的指針并試圖在dispatch表找到選擇器。一直找不到選擇器,objc_msgSend將一直查找類的層次結(jié)構(gòu),直到NSObject類。一旦定位到選擇器,函數(shù)將調(diào)用表中的方法,并將其傳遞到接收對(duì)象的數(shù)據(jù)結(jié)構(gòu)。

運(yùn)行時(shí)選擇以這種方式實(shí)現(xiàn)方法。或者以面向?qū)ο缶幊绦g(shù)語(yǔ)來說,該方法是動(dòng)態(tài)綁定到消息。

為了加快消息傳遞過程,運(yùn)行時(shí)系統(tǒng)緩存使用的方法的選擇器和地址。每個(gè)類有一個(gè)單獨(dú)的緩存,可以包含繼承方法和類中定義方法的選擇器。在搜索dispatch表之前,消息傳遞程序首先檢查接收對(duì)象類(理論上,是有可能再次使用的方法)的緩存。如果方法選擇器在緩存中,消息傳遞稍微比函數(shù)調(diào)用慢。一旦一個(gè)程序運(yùn)行足夠長(zhǎng)時(shí)間來“熱身”緩存,幾乎所有發(fā)送的消息都能找到緩存方法。在程序運(yùn)行時(shí),緩存能動(dòng)態(tài)適應(yīng)新消息。

使用隱藏的參數(shù)

當(dāng)objc_msgSend發(fā)現(xiàn)實(shí)現(xiàn)方法的程序,它調(diào)用程序,并傳遞消息中所有的參數(shù)。也傳遞兩個(gè)隱藏參數(shù)到程序:

  • 接收對(duì)象

  • 方法選擇器

這些參數(shù)為每個(gè)方法實(shí)現(xiàn)提供明確信息,這些信息關(guān)于調(diào)用它們的消息表達(dá)式。它們被認(rèn)為是“隱藏”的,因?yàn)榉椒ǘx代碼中未聲明它們。當(dāng)編譯代碼時(shí),它們插入到實(shí)現(xiàn)中。

盡管這些參數(shù)沒有顯式的聲明,源代碼仍然可以引用它們(就像它可以引用接收對(duì)象的實(shí)例變量)。方法引用接收對(duì)象作為self,以及自己的選擇器作為_cmd。在下面的例子中,_cmd引用strange 方法的選擇器,self引用接收一個(gè)strange 消息的對(duì)象。
<pre><code>

  • strange
    {
    id target = getTheReceiver();
    SEL method = getTheMethod();

    if ( target == self || method == _cmd )
    return nil;
    return [target performSelector:method];
    }

</pre></code>

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

獲取方法地址

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

NSObject類中定義一個(gè)methodForSelector:方法,可以訪問指向?qū)崿F(xiàn)方法程序的指針,然后使用指針調(diào)用該程序。methodForSelector:指針的返回值必須指向合適的函數(shù)類型。必須包含返回值和參數(shù)類型。

下面的例子展示了程序如何實(shí)現(xiàn)setFilled: 方法:

<pre><code>
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);

</pre></code>

前兩個(gè)參數(shù)傳遞給接收對(duì)象(self)的程序和方法選擇器(_cmd)。這些參數(shù)在方法語(yǔ)法中是隱藏的,但當(dāng)該方法當(dāng)成函數(shù)調(diào)用時(shí),必須是顯式的。

使用methodForSelector:方法避免動(dòng)態(tài)綁定節(jié)省了消息轉(zhuǎn)發(fā)所需的大部分時(shí)間。然而,只有在特定消息重復(fù)多次的情況下,如上面的for循環(huán),節(jié)省時(shí)間才會(huì)有重要意義。

注意:Cocoa運(yùn)行時(shí)系統(tǒng)提供methodForSelector:方法,該方法并不是Objective-C 語(yǔ)言本身的特性。

官方原文地址:

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtHowMessagingWorks.html#//apple_ref/doc/uid/TP40008048-CH104-SW1

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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