Swift派發(fā)機(jī)制

Swift派發(fā)分:靜態(tài)派發(fā)和動(dòng)態(tài)派發(fā)

靜態(tài)派發(fā):(又叫:直接調(diào)用)

靜態(tài)派發(fā)機(jī)制层玲,同時(shí)支持值類(lèi)型和引用類(lèi)型抱冷;靜態(tài)派發(fā)是最快的, 不止是因?yàn)樾枰{(diào)用的指令集會(huì)更少, 并且編譯器還能夠有很大的優(yōu)化空間, 例如函數(shù)內(nèi)聯(lián)等,. 靜態(tài)派發(fā)也有人稱(chēng)為直接調(diào)用.

然而, 對(duì)于編程來(lái)說(shuō)直接調(diào)用也是最大的局限, 而且因?yàn)槿狈?dòng)態(tài)性所以沒(méi)辦法支持繼承

動(dòng)態(tài)派發(fā):

動(dòng)態(tài)派發(fā)機(jī)制僅支持引用類(lèi)型(reference types) 比如:Class如捅。簡(jiǎn)而言之:對(duì)于動(dòng)態(tài)性或者動(dòng)態(tài)派發(fā)袭祟,我們需要使用到繼承特性亏掀,而這是值類(lèi)型不支持的缸榄。動(dòng)態(tài)派發(fā)是調(diào)用函數(shù)最動(dòng)態(tài)的方式. 也是 Cocoa 的基石, 這樣的機(jī)制催生了 KVO, UIAppearence 和 CoreData 等功能. 這種運(yùn)作方式的關(guān)鍵在于開(kāi)發(fā)者可以在運(yùn)行時(shí)改變函數(shù)的行為. 不止可以通過(guò) swizzling 來(lái)改變, 甚至可以用 isa-swizzling 修改對(duì)象的繼承關(guān)系, 可以在面向?qū)ο蟮幕A(chǔ)上實(shí)現(xiàn)自定義派發(fā).

函數(shù)表派發(fā):

函數(shù)表派發(fā)是編譯型語(yǔ)言實(shí)現(xiàn)動(dòng)態(tài)行為最常見(jiàn)的實(shí)現(xiàn)方式. 函數(shù)表使用了一個(gè)數(shù)組來(lái)存儲(chǔ)類(lèi)聲明的每一個(gè)函數(shù)的指針. 大部分語(yǔ)言把這個(gè)稱(chēng)為 "virtual table"(虛函數(shù)表), Swift 里稱(chēng)為 "witness table". 每一個(gè)類(lèi)都會(huì)維護(hù)一個(gè)函數(shù)表, 里面記錄著類(lèi)所有的函數(shù), 如果父類(lèi)函數(shù)被 override 的話, 表里面只會(huì)保存被 override 之后的函數(shù). 一個(gè)子類(lèi)新添加的函數(shù), 都會(huì)被插入到這個(gè)數(shù)組的最后. 運(yùn)行時(shí)會(huì)根據(jù)這一個(gè)表去決定實(shí)際要被調(diào)用的函數(shù).

查表是一種簡(jiǎn)單, 易實(shí)現(xiàn), 而且性能可預(yù)知的方式. 然而, 這種派發(fā)方式比起靜態(tài)派發(fā)還是慢一點(diǎn). 從字節(jié)碼角度來(lái)看, 多了兩次讀和一次跳轉(zhuǎn), 由此帶來(lái)了性能的損耗. 另一個(gè)慢的原因在于編譯器可能會(huì)由于函數(shù)內(nèi)執(zhí)行的任務(wù)導(dǎo)致無(wú)法優(yōu)化. (如果函數(shù)帶有副作用的話)

這種基于數(shù)組的實(shí)現(xiàn), 缺陷在于函數(shù)表無(wú)法拓展. 子類(lèi)會(huì)在虛數(shù)函數(shù)表的最后插入新的函數(shù), 沒(méi)有位置可以讓 extension 安全地插入函數(shù). 這篇提案很詳細(xì)地描述了這么做的局限.

4種派發(fā)機(jī)制:

1渤弛、內(nèi)聯(lián)(inline)最快

2、靜態(tài)派發(fā)(Static Dispatch)

3甚带、函數(shù)表派發(fā)(Virtual Dispatch)

4暮芭、動(dòng)態(tài)派發(fā)(Dynamic Dispatch)(最慢)

由編譯器決定應(yīng)該使用哪種派發(fā)技術(shù)。當(dāng)然欲低,優(yōu)先選擇內(nèi)聯(lián)辕宏,然后按需選擇

靜態(tài)派發(fā)VS動(dòng)態(tài)派發(fā)Swift VS OC

OC默認(rèn)支持動(dòng)態(tài)派發(fā),這種派發(fā)形式以多態(tài)的形式為開(kāi)發(fā)人員提供了靈活性砾莱。

比如:子類(lèi)可以重寫(xiě)父類(lèi)的方法

動(dòng)態(tài)派發(fā)以一定的運(yùn)行時(shí)開(kāi)銷(xiāo)為代價(jià)瑞筐,提供了語(yǔ)言的靈活性。

在動(dòng)態(tài)派發(fā)機(jī)制下腊瑟,對(duì)于每個(gè)方法的調(diào)用聚假,編譯器必須在方法列表中查找執(zhí)行方法的實(shí)現(xiàn)。

編譯器需要判斷調(diào)用方闰非,是選擇父類(lèi)的實(shí)現(xiàn)還是子類(lèi)的實(shí)現(xiàn)膘格,而且由于所有對(duì)象的內(nèi)存都是在運(yùn)行時(shí)分配的,因此編譯器只能在運(yùn)行時(shí)執(zhí)行檢查财松。

而靜態(tài)調(diào)用則沒(méi)有這個(gè)問(wèn)題瘪贱。在編譯期的時(shí)候,編譯器就知道要為某個(gè)方法調(diào)用某種實(shí)現(xiàn)辆毡。因此編譯器可以執(zhí)行某些優(yōu)化菜秦,甚至在可能的情況下,可以將某些代碼轉(zhuǎn)換成inline函數(shù)舶掖,從而使整體執(zhí)行速度更快

如何在Swift中使用動(dòng)態(tài)派發(fā)和靜態(tài)派發(fā)球昨?

1、要實(shí)現(xiàn)動(dòng)態(tài)派發(fā)眨攘,可以使用繼承主慰,重寫(xiě)父類(lèi)的方法嚣州。另外我們可以使用dynamic關(guān)鍵字,并且需要在方法或類(lèi)前面加上關(guān)鍵字@objc共螺,以便方法公開(kāi)給OC runtime使用

2避诽、要實(shí)現(xiàn)靜態(tài)派發(fā),我們可以使用final和static關(guān)鍵字璃谨,保證不會(huì)被覆寫(xiě)

靜態(tài)派發(fā):

和動(dòng)態(tài)派發(fā)相比沙庐,非常快佳吞。編譯器可以在編譯器定位到函數(shù)的位置拱雏。因此函數(shù)被調(diào)用時(shí),編譯器能通過(guò)函數(shù)的內(nèi)存地址底扳,直接找到它的函數(shù)實(shí)現(xiàn)铸抑。極大的提高了性能,可以達(dá)到類(lèi)型inline的編譯期優(yōu)化

動(dòng)態(tài)派發(fā):

在這種類(lèi)型的派發(fā)中衷模,在運(yùn)行時(shí)而不是編譯時(shí)選擇實(shí)現(xiàn)方法鹊汛,會(huì)增加運(yùn)行時(shí)的性能開(kāi)銷(xiāo)。

優(yōu)勢(shì)是:具有靈活性(大多數(shù)的OOP語(yǔ)言都支持動(dòng)態(tài)派發(fā)阱冶,因?yàn)樗试S多態(tài))

動(dòng)態(tài)派發(fā)有兩種形式:

1刁憋、函數(shù)表派發(fā)(Table Dispatch)

這種調(diào)用方式利用一個(gè)表,該表是一組函數(shù)指針木蹬,稱(chēng)為witness table至耻,以查找特定方法的實(shí)現(xiàn)

witness table如何工作?

每個(gè)子類(lèi)都有它自己的表結(jié)構(gòu)

對(duì)于類(lèi)中每個(gè)重寫(xiě)的方法镊叁,都有不同的函數(shù)指針

當(dāng)子類(lèi)添加新方法時(shí)尘颓,這些方法指針會(huì)添加在表數(shù)組的末尾

最后,編譯器在運(yùn)行時(shí)使用此表來(lái)查找調(diào)用函數(shù)的實(shí)現(xiàn)

由于編譯器必須從表中讀取方法實(shí)現(xiàn)的內(nèi)存地址晦譬,然后跳轉(zhuǎn)到該地址疤苹,一次它需兩條附加指令,因此它比靜態(tài)派發(fā)慢敛腌,但仍比消息派發(fā)快

2卧土、消息派發(fā)(Message Dispatch)

這種動(dòng)態(tài)派發(fā)方式是最動(dòng)態(tài)的。事實(shí)上它表現(xiàn)優(yōu)異迎瞧,目前Cocoa框架在KVO夸溶,CoreData等很多地方在使用它

此外,它還可以使用method swizzling凶硅,可以在運(yùn)行時(shí)更改函數(shù)的實(shí)現(xiàn)。

Swift本身不支持消息派發(fā)扫皱,而是利用OC的runtime特性足绅,間接實(shí)現(xiàn)這種動(dòng)態(tài)性捷绑。

要使用動(dòng)態(tài)性需要使用dynamic關(guān)鍵字。Swift4.0之前氢妈,需要一起使用dynamic和@objc粹污。Swift4.0之后,只需表明@objc讓方法支持oc的調(diào)用首量,以支持消息派發(fā)

具體通過(guò)如下代碼來(lái)深入體會(huì):

/*

值類(lèi)型:

由于struct和enum都是值類(lèi)型壮吩,不支持繼承,編譯器將它們置為靜態(tài)派發(fā)下加缘,因?yàn)樗鼈冇肋h(yuǎn)不可能被子類(lèi)化

*/

struct LTPerson {

? ? //靜態(tài)派發(fā)

? ? func chinesePerson() -> Bool {

? ? ? ? return true

? ? }

}

extension LTPerson {

? ? ////靜態(tài)派發(fā)

? ? func canDispatch() -> Bool {

? ? ? ? return true

? ? }

}

/*

協(xié)議

這里重點(diǎn)是在extension(擴(kuò)展)里面定義的函數(shù)鸭叙,使用靜態(tài)派發(fā)(static dispatch)

*/

protocol LTAnimal{

? ? //函數(shù)表派發(fā)

? ? func isAnimal() -> Bool

}

extension LTAnimal{

? ? //靜態(tài)派發(fā)

? ? func canDispatch() -> Bool{

? ? ? ? return true

? ? }

}

/*

類(lèi)

普通方法聲明遵循協(xié)議的規(guī)則

當(dāng)將方法公開(kāi)給OC runtime是使用@objc,使用動(dòng)態(tài)派發(fā)

當(dāng)一個(gè)類(lèi)表標(biāo)記為final是拣宏,該類(lèi)不能被子類(lèi)化沈贝,使用靜態(tài)派發(fā)

*/

class LTCat: LTAnimal{

? ? //函數(shù)表派發(fā)

? ? func isAnimal() -> Bool {

? ? ? ? return true

? ? }


? ? //動(dòng)態(tài)派發(fā)

? ? @objc dynamic func hoursSleep() -> Int{

? ? ? ? return12

? ? }

}

extension LTCat{

? ? //靜態(tài)派發(fā)

? ? func canSwim() -> Bool{

? ? ? ? return false

? ? }

? ? //動(dòng)態(tài)派發(fā)

? ? @objc func goWild(){


? ? }

}

final class LTEmployee{

? ? //靜態(tài)派發(fā)

? ? func canSpeak()->Bool{

? ? ? ? return true

? ? }

}

通過(guò)上面的代碼可以知道是使用的哪種派發(fā)方式;具體可以通過(guò)sil文件來(lái)進(jìn)行驗(yàn)證

生成SIL文件命令:swiftc -emit-sil main.swift >> main.sil

如果使用table派發(fā)勋乾,則會(huì)出現(xiàn)在value(或者witness_table)中

vtable

如果使用動(dòng)態(tài)轉(zhuǎn)發(fā)宋下,則能找到objc_method標(biāo)記,指示使用OC運(yùn)行時(shí)調(diào)用了該函數(shù)

objc_method

如果沒(méi)有出現(xiàn)以上兩種情況的標(biāo)記辑莫,則是靜態(tài)派發(fā)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末学歧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子各吨,更是在濱河造成了極大的恐慌撩满,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绅你,死亡現(xiàn)場(chǎng)離奇詭異伺帘,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)忌锯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)伪嫁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人偶垮,你說(shuō)我怎么就攤上這事张咳。” “怎么了似舵?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵脚猾,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我砚哗,道長(zhǎng)龙助,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任蛛芥,我火速辦了婚禮提鸟,結(jié)果婚禮上军援,老公的妹妹穿的比我還像新娘。我一直安慰自己称勋,他們只是感情好胸哥,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著赡鲜,像睡著了一般空厌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上银酬,一...
    開(kāi)封第一講書(shū)人閱讀 52,713評(píng)論 1 312
  • 那天嘲更,我揣著相機(jī)與錄音,去河邊找鬼捡硅。 笑死哮内,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的壮韭。 我是一名探鬼主播北发,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼喷屋!你這毒婦竟也來(lái)了琳拨?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤屯曹,失蹤者是張志新(化名)和其女友劉穎狱庇,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體恶耽,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡密任,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了偷俭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浪讳。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖涌萤,靈堂內(nèi)的尸體忽然破棺而出淹遵,到底是詐尸還是另有隱情,我是刑警寧澤负溪,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布透揣,位于F島的核電站,受9級(jí)特大地震影響川抡,放射性物質(zhì)發(fā)生泄漏辐真。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拆祈。 院中可真熱鬧恨闪,春花似錦倘感、人聲如沸放坏。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)淤年。三九已至,卻和暖如春蜡豹,著一層夾襖步出監(jiān)牢的瞬間麸粮,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工镜廉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留弄诲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓娇唯,卻偏偏與公主長(zhǎng)得像齐遵,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子塔插,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

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