前言
在忙碌中抽取時(shí)間來看這篇文章的朋友,希望各位看完這篇問斬都能都有所收獲。這篇文章主要是給大家分享的是Swift結(jié)構(gòu)體與類的方法調(diào)度。好了廢話不多說,接下來直接步入主題吧。
1.普通方法時(shí)兩者方法調(diào)度的區(qū)別
● 結(jié)構(gòu)體中的普通方法調(diào)度是靜態(tài)派發(fā)的方式
○ 詳細(xì)分析會在以后: 方法調(diào)度之普通結(jié)構(gòu)體方法 闡述
● 類中的普通方法是以函數(shù)派發(fā)的方式去調(diào)度的。
○ 詳細(xì)分析會在以后:方法調(diào)度之普通方法 闡述
2.議中兩者方法調(diào)度的區(qū)別
● 以類/結(jié)構(gòu)體直接聲明的,
○ 結(jié)構(gòu)體:方法調(diào)度都是靜態(tài)調(diào)度
○ 類:方法調(diào)度都是函數(shù)調(diào)度
● 以協(xié)議類型聲明的, 無論協(xié)議的實(shí)現(xiàn)是類還是結(jié)構(gòu)體:
○ 方法最初定義在協(xié)議本身內(nèi), 則方法以協(xié)議函數(shù)表的方式調(diào)度
○ 方法最初定義在協(xié)議延展內(nèi), 則方法以靜態(tài)派發(fā)的方式調(diào)度
3.extension對類中方法調(diào)度的影響
``extension
PersonClass
{
``func
changClassName10``() {}
}
SIL代碼:
[圖片上傳失敗...(image-1dcbb3-1634395436453)]
[圖片上傳失敗...(image-a6e24-1634395436453)]
斷點(diǎn),匯編跟蹤一下:
[圖片上傳失敗...(image-50578a-1634395436453)]
可以看到 changClassName10 這個(gè)方法在執(zhí)行的時(shí)候,由function_ref修飾,sil_vtable 中的函數(shù)列表里面沒有。在編譯時(shí)已經(jīng)確定了函數(shù)的地址,運(yùn)行時(shí),直接執(zhí)行。所以延展內(nèi)的方法是靜態(tài)派發(fā)。
??****思考: 為什么普通函數(shù)放到了延展中,它就不在函數(shù)表中,不是函數(shù)派發(fā)的方式調(diào)度了呢?
我們在[方法調(diào)度之普通方法:后續(xù)筆者會發(fā)布普通方法調(diào)用過程]:函數(shù)表是數(shù)組結(jié)構(gòu),里面的函數(shù)是按順序排列的。
如果父類存在延展方法,且放在函數(shù)表里,就需要考慮它和子類方法的排列順序問題。哪個(gè)在前,取決于文件的編譯順序。如果子類先編譯,父類后編譯,還要將子類的所有方法都順次移位,再將延展方法插入到父類方法之后。這樣做,編譯效率就會降低。將延展方法使用靜態(tài)派發(fā),是一種以空間換時(shí)間的方法。 協(xié)議的延展中的方法,也是靜態(tài)派發(fā)的,他們是一樣的道理。
【注意】類的延展方法時(shí),需要注意:
- 不可以在子類里重寫父類延展里面的方法,子類可以重寫父類本類定義的方法
- 不可以在延展里 存在/重寫 已在繼承連中存在的同名方法
[圖片上傳失敗...(image-5410e6-1634395436453)]
4.修飾詞對類方法調(diào)度的影響
1. 訪問修飾符修飾的方法
private
func
changClassName2``() {}
fileprivate
func
changClassName3``() {}
public
func
changClassName4``() {}
internal
func
changClassName5``() {}
open
func
changClassName6``() {}
SIL 代碼:
sil_vtable SIL :
[圖片上傳失敗...(image-5ddce8-1634395436453)]
雖然所有函數(shù)修飾符修飾的方法,都在函數(shù)表中存在,但是明顯 private 修飾的 changClassName2
, 與 fileprivate 修飾的changClassName3
與眾不同,他們在方法名的后面有 in _12232F587A4C5CD8B1EEDF696793A4FC
****。 這個(gè)不同,會導(dǎo)致它們在方法調(diào)度的時(shí)候,和其他的訪問修飾符什么區(qū)別呢?
再看方法調(diào)度 SIL :
[圖片上傳失敗...(image-b50363-1634395436453)]
可以發(fā)現(xiàn) private 修飾的 changClassName2
, 與 fileprivate 修飾的changClassName3
在調(diào)用時(shí),前面的修飾符是由function_ref 修飾 , 而不是class_method修飾。所以是靜態(tài)派發(fā)?
再匯編調(diào)試一下:
[圖片上傳失敗...(image-8148de-1634395436453)]
在編譯時(shí)已經(jīng)確定了函數(shù)的地址,運(yùn)行時(shí),直接執(zhí)行。所以private/fileprivate 訪問修飾符修飾的是靜態(tài)派發(fā)。
前面我們提到“函數(shù)表存放類中可能是動態(tài)派發(fā)去執(zhí)行的函數(shù)”, 注意是可能哦, 不是一定的。
小結(jié):
private/fileprivate 訪問修飾符修飾的是靜態(tài)派發(fā)。
public/open/internal 訪問修飾符修飾的是函數(shù)派發(fā)。
2. @objc 修飾的方法: 函數(shù)表
源碼:@objc
func
changClassName7``() {}
vtable SIL:
[圖片上傳失敗...(image-d6971f-1634395436453)]
方法調(diào)度 SIL:
[圖片上傳失敗...(image-cdcda3-1634395436453)]
運(yùn)行、匯編:
[圖片上傳失敗...(image-a031ef-1634395436453)]
所以: 在swift 中調(diào)用 @objc 修飾的方法是函數(shù)派發(fā),沒什么特別的。
那 @objc 的作用是什么呢?
我們來看一下changClassName7 方法定義在 SIL 代碼:
[圖片上傳失敗...(image-b852b5-1634395436453)]
可以看到,除了正常的定義changClassName7 方法以外,額外底層多生成了一個(gè) @objc main.PersonClass.changClassName7()
這個(gè)方法內(nèi)部又調(diào)用了 正常定義的changClassName7。
所以這個(gè)方法是暴露給OC中調(diào)用的接口方法. 沒有@objc 修飾的方法,OC 中是無法使用的。
3. dynamic 修飾的方法:函數(shù)表
源碼如下:
dynamic
func
changClassName8``() {}
vtable SIL:
[圖片上傳失敗...(image-f62e21-1634395436453)]
方法調(diào)度 SIL:
[圖片上傳失敗...(image-7b8f92-1634395436453)]
運(yùn)行、匯編:
[圖片上傳失敗...(image-3bbd4f-1634395436453)]
在編譯時(shí),不能確定方法的地址,在函數(shù)表內(nèi),所以 dynamic的方法調(diào)度方式 是函數(shù)派發(fā)。
dynamic 有什么作用呢?
看看方法定義SIL:
[圖片上傳失敗...(image-b25c4b-1634395436453)]
與普通函數(shù)不同的是,在方法定義時(shí),多了一個(gè)dynamically_replacable
的標(biāo)簽,表明這是一個(gè)動態(tài)方法,可以被替換。可被替換是指在OC運(yùn)行時(shí)的方法交換的場景下可被替換。
如果想要對Swift 方法進(jìn)行方法交換,需要對被替換的方法加dynamic修飾。
再使用@_dynamicReplacement(for: teach)
來完成替換.
示例代碼如下:
````classPersonClass
:
NSObject{`
``dynamic
func
teach``() {
``print``(``"teach"``)
``}
}
extension
PersonClass
{
``// swift 5 中提供的方法交換方式
``// 將 teach 方法替換成這行代碼下面的teach1方法
``// 執(zhí)行 teach 方法,實(shí)際上執(zhí)行的是 teach1方法
``@``_dynamicReplacement``(``for``: ``teach``)
``func
teach1``() {
``print``(``"teach1"``)
``}
}
let
t
= ``PersonClass``()
t
.teach()
所以打印結(jié)果是:“teach1”
[圖片上傳失敗...(image-f74690-1634395436453)]
4. @objc dynamic 修飾的方法:消息轉(zhuǎn)發(fā)
源碼如下:
1 |
@objc dynamic func changClassName9``() {}
|
---|
vtable SIL:
[圖片上傳失敗...(image-9721e7-1634395436453)]
函數(shù)表中沒有changClassName9的函數(shù)。
方法調(diào)度 SIL:
[圖片上傳失敗...(image-5abe6-1634395436453)]
與普通的函數(shù)派發(fā)方法調(diào)用時(shí)不同,不是以 class_method 方式,是以objc_method
方式
運(yùn)行、匯編調(diào)試:
[圖片上傳失敗...(image-5b1001-1634395436453)]
匯編調(diào)試時(shí),看到了熟悉的objc_msgSend
。這是OC的消息轉(zhuǎn)發(fā)的方式進(jìn)行方法調(diào)度。
5. static 修飾
static修飾的方法,叫做類方法,可以直接由類名去調(diào)用,無需創(chuàng)建實(shí)例對象。
源碼如下:
1 |
static func changClassName11``() {}
|
---|
vtable SIL:
[圖片上傳失敗...(image-21da64-1634395436453)]
方法調(diào)度 SIL:
[圖片上傳失敗...(image-8a3771-1634395436453)]
運(yùn)行、匯編調(diào)試:
[圖片上傳失敗...(image-34e219-1634395436453)]
以function_ref 的方式獲取函數(shù), 所以是靜態(tài)派發(fā)
6. final 修飾
final修飾符的幾點(diǎn)使用原則
- final修飾符只能修飾類,表明該類不能被其他類繼承,也就是它沒資格當(dāng)父類。
- final修飾符也可以修飾類中的方法, 表明該方法不能被子類重寫。
- final不能修飾結(jié)構(gòu)體、枚舉、協(xié)議。
源碼如下:
源碼如下:
1 |
final func changClassName1``() {}
|
---|
table SIL:
[圖片上傳失敗...(image-f3ceea-1634395436453)]
方法調(diào)度 SIL:
[圖片上傳失敗...(image-d02d0a-1634395436453)]
行、匯編調(diào)試:
[圖片上傳失敗...(image-807e0b-1634395436453)]
以function_ref 的方式獲取函數(shù), 所以是靜態(tài)派發(fā)
5.總結(jié)
函數(shù)表內(nèi)的函數(shù),不一定是函數(shù)派發(fā)的方式去調(diào)度。但是不在函數(shù)表中的,一定不是函數(shù)派發(fā)的方式。
在調(diào)用時(shí)獲取函數(shù)的方式可以作為判斷調(diào)度方法的依據(jù)。下面是對應(yīng)不同的獲取函數(shù)的方式的不同調(diào)度方式:
Swift 中的方法調(diào)度分2大類:動態(tài) 調(diào)度 與靜態(tài) 調(diào)度
- Direct(靜態(tài)調(diào)度): 在 SIL 文件中,以function_ref 的方式獲取函數(shù)
- ?? 結(jié)構(gòu)體的普通方法
- 類中方法的修飾符為 : final / private/fileprivate / static
- 類、結(jié)構(gòu)體、協(xié)議延展內(nèi)的方法
- Dynamic Dispatch( 動態(tài)調(diào)度 ) : 官方文檔傳送門? Dynamic Dispatch
- Table(函數(shù)表調(diào)度) : 在 SIL 文件中,以 class_method 的方式,通過 Vtable 獲取函數(shù)
- ?? 普通類中的方法
- 類中方法的修飾符為: open/public/internal / @objc / dynamaic
- Message(消息轉(zhuǎn)發(fā)調(diào)度): 在 SIL 文件中,以 objc_method 的方式獲取函數(shù)
- ?? @objc dynamaic
- witness_method(協(xié)議表調(diào)度): 在 SIL 文件中,以 witness_method 的方式, 通過 PWT 獲取函數(shù)
- ?? 遵守了協(xié)議并實(shí)現(xiàn)了協(xié)議本身定義的方法的結(jié)構(gòu)體或者類
好了,小編給大家整理的swift的結(jié)構(gòu)體與類的方法調(diào)度,若有收獲,就點(diǎn)個(gè)贊吧!
青山不改,綠水長流,后會有期,感謝每一位佳人的支持!更多iOS知識請關(guān)注主頁。