iOS:Runtime - objc_msgSend函數(shù)

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

消息傳遞

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

objc_msgSend函數(shù)

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

[receiver message]

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

objc_msgSend(receiver, selector)

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

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

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

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

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

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

  • 指向超類的指針杖们。

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

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


image.png

當(dāng)消息發(fā)送到對象時闻察,消息傳遞功能將跟隨對象的 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)用在表中輸入的方法,并將該方法傳遞給接收對象的數(shù)據(jù)結(jié)構(gòu)缨睡。

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

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

使用隱藏參數(shù)

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

  • 接收對象
  • 選擇器 對于方法

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

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

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

獲取方法地址

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

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

下面的示例顯示了如何setFilled:調(diào)用實現(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);

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

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

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

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刃跛,一起剝皮案震驚了整個濱河市抠艾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌桨昙,老刑警劉巖跌帐,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绊率,居然都是意外死亡,警方通過查閱死者的電腦和手機究履,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門滤否,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人最仑,你說我怎么就攤上這事藐俺〈都祝” “怎么了?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵欲芹,是天一觀的道長卿啡。 經(jīng)常有香客問我,道長菱父,這世上最難降的妖魔是什么颈娜? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮浙宜,結(jié)果婚禮上官辽,老公的妹妹穿的比我還像新娘。我一直安慰自己粟瞬,他們只是感情好同仆,可當(dāng)我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著裙品,像睡著了一般俗批。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上市怎,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天岁忘,我揣著相機與錄音,去河邊找鬼焰轻。 笑死臭觉,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辱志。 我是一名探鬼主播蝠筑,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼揩懒!你這毒婦竟也來了什乙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤已球,失蹤者是張志新(化名)和其女友劉穎臣镣,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體智亮,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡忆某,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了阔蛉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弃舒。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出聋呢,到底是詐尸還是另有隱情苗踪,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布削锰,位于F島的核電站通铲,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏器贩。R本人自食惡果不足惜颅夺,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望磨澡。 院中可真熱鬧碗啄,春花似錦、人聲如沸稳摄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厦酬。三九已至胆描,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間仗阅,已是汗流浹背昌讲。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留减噪,地道東北人短绸。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像筹裕,于是被迫代替她去往敵國和親醋闭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,492評論 2 348

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