Swift方法調(diào)用

在了解Swift發(fā)布調(diào)用機制之前煌集,先來了解下swift方法是如何保存的叁扫。

在swift中所有數(shù)據(jù)類型的無外乎兩種:值類型,引用類型地来。

值類型 : 在內(nèi)存中直接保存值乘寒,有點類似oc中taggedPointer
引用類型 : 在內(nèi)存中保存指針地址

image

編程語言 函數(shù)調(diào)用機制有三種:
  • 直接調(diào)用
  • 函數(shù)表調(diào)用
  • 消息派發(fā)機制調(diào)用

函數(shù)調(diào)用機制是程序判斷使用哪種途徑去調(diào)用一個函數(shù)的機制万栅,每次函數(shù)調(diào)用時都會被觸發(fā)佑钾。
了解函數(shù)的調(diào)用機制,對于寫出高性能代碼來說十分有必要烦粒。
Java:默認函數(shù)表調(diào)用休溶,可以通過final修飾修改成直接派發(fā)。
C++:默認是直接調(diào)用扰她,但可以通過virtual修飾符來改成函數(shù)表派發(fā)
Objective-C:總是使用消息派發(fā)的機制兽掰,但允許開發(fā)者使用C直接派發(fā)來獲取性能的提高。

Swift 函數(shù)調(diào)用機制徒役,以上 三種都會涉及到孽尽。

調(diào)用方式:(Types of Dispatch)

程序派發(fā)的目的是為了告訴CPU需要被調(diào)用的函數(shù)在哪里。在我們深入理解Swift派發(fā)機制之前忧勿,先來了解下這三種派發(fā)機制杉女,以及沒中方式在動態(tài)性和性能之間的取舍。

直接調(diào)用:

直接派發(fā)是最快的鸳吸,不止是因為需要調(diào)用的指令集少宠纯,并且編譯器還有很大的優(yōu)化空間(比如:函數(shù)內(nèi)聯(lián))。直接派發(fā)也稱為靜態(tài)調(diào)用层释。
然而,對于編程來說直接調(diào)用也是最大的局限快集,而且因為缺乏動態(tài)性所以沒辦法支持繼承贡羔。

函數(shù)表調(diào)用

函數(shù)表派發(fā)是編譯型語言實現(xiàn)動態(tài)行為最常見的實現(xiàn)方式廉白。函數(shù)表使用了一個數(shù)組來儲存類聲明的每一個函數(shù)的指針。大部分語言把這個稱為『virtual table』虛函數(shù)表乖寒,Swift里稱為 『witness table』猴蹂。每一個類都會維護一個虛函數(shù)表,里邊記錄著類的所有函數(shù)楣嘁,如果父類被override的話磅轻,表里只會保存override之后的函數(shù),子類新增后會被插到這個數(shù)組的最后逐虚,運行時會決定這一個表實際要被調(diào)用的函數(shù)聋溜。
當一個函數(shù)調(diào)用時,首先要讀取函數(shù)表叭爱,在讀取函數(shù)對應的索引撮躁,然后跳轉(zhuǎn)。

消息派發(fā)機制調(diào)用

消息機制是調(diào)用函數(shù)的最動態(tài)的方式买雾,也是Cocoa的基礎把曼,這樣的機制催生了KVO、UIAppearence漓穿、CoreData等功能嗤军,這種運作方式的關鍵在于開發(fā)者可以在運行時改變函數(shù)的行為,不止可以通過swizzling 來改變晃危,還可以用 isa-swizzling修改對象的繼承關系叙赚,可以在面向?qū)ο蟮幕A上實現(xiàn)自定義派發(fā)。

Struct方法調(diào)用

示例代碼如下:

image

通過調(diào)試信息可知山害,內(nèi)存中只存儲了變量age纠俭,并未存儲方法。

image-20210518180344896

打開匯編堆棧浪慌,可知funcDemo方法的地址0x10a242430在代碼段中冤荆,其在符號表中的名稱為s11SwiftMethod9StructOneV8funcDemoyyF

image-20210518180422313

(注:cat address指令需要安裝lldb擴展,點擊這里下載

使用image list命令獲取到系統(tǒng)默認的地址偏移量0x000000010a23f000

image-20210518180445508

使用方法地址0x10a242430減去默認的地址偏移量0x000000010a23f000权纤,得到該方法真實的地址0x3430

此時查看程序的MachO文件钓简,在TEXT段中查看0x3430,可以看出右圖框中的部分即為方法funcDemo的匯編代碼汹想。

7D88E609E9060D842517272C791167C6

再點擊Xcode的step into按鈕進入方法內(nèi)部并查看其匯編代碼外邓。

image-20210518180604511

通過比較二者的匯編代碼,可知此時程序執(zhí)行的就是預先生成好的匯編代碼古掏。

再將在MachO文件的符號表中進行查找s11SwiftMethod9StructOneV8funcDemoyyF损话,也可以知道其地址為0x3430

image-20210421155514739

由此可以看出結(jié)構(gòu)體方法是在程序編譯鏈接時就直接在符號表中生成好,調(diào)用時無需額外操作丧枪。

按照同樣的步驟光涂,struct + protocol & struct + extension 也是直接調(diào)用。

枚舉類方法調(diào)用

定義如下枚舉類

image

打開匯編調(diào)試

image

由匯編指令可知拧烦,枚舉類的方法是直接調(diào)用的忘闻。

Class 方法調(diào)用

定義如下類:

class ClassMethodModel {
    func funcOne() {
        print("funcOne")
    }
    func funcTwo() {
        print("funcTwo")
    }
}
let cm = ClassMethodModel()
cm.funcOne()
cm.funcTwo()

使用 xcrun swiftc -emit-sil ClassMethod.swift 得到 swift代碼 編譯后的 sil 產(chǎn)物,可以看出class的方法是存在vtable中的恋博。


sil_vtable ClassMethodModel {
  #ClassMethodModel.funcOne: (ClassMethodModel) -> () -> () : @$s11ClassMethod0aB5ModelC7funcOneyyF // ClassMethodModel.funcOne()
  #ClassMethodModel.funcTwo: (ClassMethodModel) -> () -> () : @$s11ClassMethod0aB5ModelC7funcTwoyyF // ClassMethodModel.funcTwo()
  #ClassMethodModel.init!allocator: (ClassMethodModel.Type) -> () -> ClassMethodModel : @$s11ClassMethod0aB5ModelCACycfC    // ClassMethodModel.__allocating_init()
  #ClassMethodModel.deinit!deallocator: @$s11ClassMethod0aB5ModelCfD    // ClassMethodModel.__deallocating_deinit
}

借助之前分析Struct和Enum的經(jīng)驗齐佳,先來看一下class的匯編調(diào)試代碼

image

從圖中可以看出,rax即為變量 cm债沮,0x45e9為funcOne的地址炼吴,0x45d9為funcTwo的地址。

對此可以得出結(jié)論:swift的class方法調(diào)用不是直接調(diào)用秦士,而是對變量進行地址偏移后找到方法指針缺厉,再進行調(diào)用。

messageSend 調(diào)用

設計了如下Demo程序隧土,模擬OC調(diào)用Swift代碼提针。

image
i

查看其斷點處的匯編代碼:

image

可知OC調(diào)用swift @objc方法是通過objc_msgSend進行的。

反過來使用swift調(diào)用swift @objc方法曹傀,Demo代碼如下:

image

其斷點處的匯編代碼為:

image

根據(jù)匯編代碼辐脖,結(jié)合上文分析class方法調(diào)用的匯編代碼,可知swift調(diào)用swift @objc方法是函數(shù)表調(diào)用皆愉。

調(diào)用方式總結(jié)

Struct & Enum Class
普通方法 直接調(diào)用 函數(shù)表調(diào)用
protocol協(xié)議 直接調(diào)用 函數(shù)表調(diào)用
extension拓展 直接調(diào)用 直接調(diào)用
final - 直接調(diào)用
繼承方法 - 函數(shù)表調(diào)用
@objc - Swift調(diào)用Swift為函數(shù)表調(diào)用 嗜价,OC調(diào)用Swift為消息轉(zhuǎn)發(fā)
dynamic - 函數(shù)表調(diào)用
@objc dynamic - objc_msgSend消息轉(zhuǎn)發(fā)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市幕庐,隨后出現(xiàn)的幾起案子久锥,更是在濱河造成了極大的恐慌,老刑警劉巖异剥,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瑟由,死亡現(xiàn)場離奇詭異,居然都是意外死亡冤寿,警方通過查閱死者的電腦和手機歹苦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來督怜,“玉大人殴瘦,你說我怎么就攤上這事『鸥埽” “怎么了蚪腋?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我屉凯,道長动遭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任神得,我火速辦了婚禮,結(jié)果婚禮上偷仿,老公的妹妹穿的比我還像新娘哩簿。我一直安慰自己,他們只是感情好酝静,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布节榜。 她就那樣靜靜地躺著,像睡著了一般别智。 火紅的嫁衣襯著肌膚如雪宗苍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天薄榛,我揣著相機與錄音讳窟,去河邊找鬼。 笑死敞恋,一個胖子當著我的面吹牛丽啡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播硬猫,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼补箍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了啸蜜?” 一聲冷哼從身側(cè)響起坑雅,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎衬横,沒想到半個月后裹粤,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡冕香,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年蛹尝,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悉尾。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡突那,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出构眯,到底是詐尸還是另有隱情愕难,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站猫缭,受9級特大地震影響葱弟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜猜丹,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一芝加、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧射窒,春花似錦藏杖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至艾疟,卻和暖如春来吩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蔽莱。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工弟疆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人碾褂。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓兽间,卻偏偏與公主長得像,于是被迫代替她去往敵國和親正塌。 傳聞我的和親對象是個殘疾皇子嘀略,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

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