多態(tài)
首先看一下結(jié)構(gòu)體的實(shí)例方法調(diào)用
看一下匯編
編譯完就能確定最終調(diào)用誰(shuí)茅信,因?yàn)楹瘮?shù)地址是寫死的强经。
換成class之后我們?cè)賮?lái)看一下
再看一下匯編
可以發(fā)現(xiàn)匯編代碼多了很多,而且方法調(diào)用時(shí)候地址也不是固定的了先口,所以在不需要使用繼承的時(shí)候樟澜,使用struct效率會(huì)高一些。
通過(guò)si進(jìn)入方法內(nèi)部
可以確定是進(jìn)入了speak方法涮阔,同理其他方法的調(diào)用也是同樣的方式猜绣。
下面正式進(jìn)入多態(tài)原理的環(huán)節(jié),先來(lái)看一下什么是多態(tài)
父類指針指向了子類敬特。
-0x48(%rbp)就是全局對(duì)象anim的地址值掰邢,%r13就是堆空間對(duì)象(Dog()生成的對(duì)象)的地址值牺陶,第二句是去堆空間對(duì)象地址取出8個(gè)字節(jié)的數(shù)據(jù)給%rax,所以%rax里面放的是dog對(duì)象內(nèi)存的前8個(gè)字節(jié)辣之,也就是類型信息掰伸,方法的內(nèi)存地址會(huì)放在類型信息里面,也就是知道了是調(diào)用父類的方法還是子類的方法怀估。
dog對(duì)象調(diào)用方法的時(shí)候狮鸭,首先會(huì)去拿類型信息,然后加上偏移量多搀,這里是0x50歧蕉,就會(huì)直接到方法的內(nèi)存地址,取出8個(gè)字節(jié)出來(lái)康铭,就是speak方法的內(nèi)存地址惯退。eat等方法的調(diào)用也是同樣的方式。
類型信息里面有一個(gè)函數(shù)表麻削,這個(gè)函數(shù)表以Dog類為例
0x50高度的內(nèi)存
Dog.speak() //8個(gè)字節(jié)
Dog.eat()
Animal.sleep()
Dog.run()
從這三次方法的調(diào)用我們可以看出偏移量都是0x50蒸痹,因?yàn)槊總€(gè)方法地址占用8個(gè)字節(jié)的內(nèi)存春弥。
無(wú)論創(chuàng)建多少個(gè)dog對(duì)象呛哟,其前8個(gè)字節(jié)都是相同的,都是一樣的類型信息匿沛。
我們代碼內(nèi)存有這么幾個(gè)空間扫责,堆空間、全局區(qū)逃呼、棻罟拢空間、代碼區(qū)抡笼。那么類型信息是存在哪個(gè)區(qū)的呢苏揣,首先排除棧空間推姻,因?yàn)樗且恢贝嬖诘牟皇钦{(diào)用完就出棧的平匈,需要一直停留在內(nèi)存當(dāng)中,我們通過(guò)匯編來(lái)看下到底是哪個(gè)區(qū)域藏古。
0x102cce879 + 0x9867 = 0x102CD80E0增炭,也就是dog對(duì)象(全局變量)的地址值,可以對(duì)比Dog的類型信息地址拧晕,非常的像隙姿。
以上是堆空間Dog對(duì)象地址值, 代碼區(qū)的地址值是匯編最左邊的地址厂捞,類似這樣0x100c36800输玷,從地址大小來(lái)劃分的話就是:代碼區(qū)队丝、全局區(qū)、堆空間欲鹏、椞棵担空間。從地址我們可以大致看出來(lái)貌虾,類型信息是在全局區(qū)吞加。
還可以利用MachOView窺探mach-o文件,來(lái)看類型信息是在TEXT段還是DATA段尽狠。
總結(jié):
Swift中多態(tài)的實(shí)現(xiàn)原理衔憨,是類似于C++的虛表,直接將對(duì)象將來(lái)要調(diào)用的方法地址放到了類型信息里袄膏,而且是編譯完就可以確定的践图。