前言
hello,小伙伴們:在忙碌中閑暇之余給大家聊聊swift的知識點(diǎn),今天給大家?guī)淼氖莝wift中結(jié)構(gòu)體與類的方法調(diào)度詳細(xì)區(qū)別绩蜻,希望對你有所幫助铣墨,好了廢話不用多說,接下來步入主題办绝!
1.普通方法時兩者方法調(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.協(xié)議中兩者方法調(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)度的影響
swift:
extension PersonClass {
func changClassName10() {}
}
SIL代碼:
斷點(diǎn)孕蝉,匯編跟蹤一下:
可以看到 changClassName10 這個方法在執(zhí)行的時候屡律,由function_ref修飾,sil_vtable 中的函數(shù)列表里面沒有降淮。在編譯時已經(jīng)確定了函數(shù)的地址超埋,運(yùn)行時,直接執(zhí)行佳鳖。所以延展內(nèi)的方法是靜態(tài)派發(fā)霍殴。
?? 思考:為什么普通函數(shù)放到了延展中,它就不在函數(shù)表中系吩,不是函數(shù)派發(fā)的方式調(diào)度了呢来庭?
我們在方法調(diào)度之普通方法一文中講解過:函數(shù)表是數(shù)組結(jié)構(gòu),里面的函數(shù)是按順序排列的淑玫。
如果父類存在延展方法巾腕,且放在函數(shù)表里面睛,就需要考慮它和子類方法的排列順序問題。哪個在前尊搬,取決于文件的編譯順序叁鉴。如果子類先編譯咸产,父類后編譯戒祠,還要將子類的所有方法都順次移位滩字,再將延展方法插入到父類方法之后轩猩。這樣做蚁堤,編譯效率就會降低住涉。將延展方法使用靜態(tài)派發(fā)杖虾,是一種以空間換時間的方法条辟。協(xié)議的延展中的方法弹渔,也是靜態(tài)派發(fā)的胳施,他們是一樣的道理。
【注意】類的延展方法時肢专,需要注意:
● 不可以在子類里重寫父類延展里面的方法舞肆,子類可以重寫父類本類定義的方法
● 不可以在延展里 存在/重寫 已在繼承連中存在的同名方法。
4.修飾詞對類方法調(diào)度的影響
1. 訪問修飾符修飾的方法
swift
private func changClassName2() {}
fileprivate func changClassName3() {}
public func changClassName4() {}
internal func changClassName5() {}
open func changClassName6() {}
SIL 代碼:
sil_vtable SIL :
雖然所有函數(shù)修飾符修飾的方法博杖,都在函數(shù)表中存在椿胯,但是明顯 private 修飾的 changClassName2 , 與 fileprivate 修飾的changClassName3 與眾不同剃根,他們在方法名的后面有** in _12232F587A4C5CD8B1EEDF696793A4FC **哩盲。 這個不同,會導(dǎo)致它們在方法調(diào)度的時候狈醉,和其他的訪問修飾符什么區(qū)別呢廉油?
再看方法調(diào)度 SIL :'
可以發(fā)現(xiàn) private 修飾的 changClassName2 , 與 fileprivate 修飾的changClassName3 在調(diào)用時苗傅,前面的修飾符是由function_ref 修飾娱两,而不是class_method修飾。所以是靜態(tài)派發(fā)?
再匯編調(diào)試一下:
在編譯時已經(jīng)確定了函數(shù)的地址金吗,運(yùn)行時十兢,直接執(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ù)表
源碼:
swift
@objc func changClassName7() {}
vtable SIL:
方法調(diào)度 SIL:
運(yùn)行、匯編:
所以: 在swift 中調(diào)用 @objc 修飾的方法是函數(shù)派發(fā)夕凝,沒什么特別的宝穗。
那 @objc 的作用是什么呢户秤?
我們來看一下changClassName7 方法定義在 SIL 代碼:
可以看到,除了正常的定義changClassName7 方法以外逮矛,額外底層多生成了一個 @objc main.PersonClass.changClassName7()
這個方法內(nèi)部又調(diào)用了 正常定義的changClassName7鸡号。
所以這個方法是**暴露給OC中調(diào)用的接口方法. 沒有@objc 修飾的方法,OC 中是無法使用的须鼎。具體的混編步驟鲸伴,可以參考這篇文章? **Swift 與 OC 混編
3. dynamic 修飾的方法:函數(shù)表
源碼如下:
swift
dynamic func changClassName8() {}
vtable SIL:
方法調(diào)度 SIL:
運(yùn)行、匯編:
在編譯時晋控,不能確定方法的地址汞窗,在函數(shù)表內(nèi),所以dynamic的方法調(diào)度方式是函數(shù)派發(fā)赡译。
dynamic 有什么作用呢仲吏?
看看方法定義SIL:
與普通函數(shù)不同的是,在方法定義時蝌焚,多了一個dynamically_replacable的標(biāo)簽蜘矢,表明這是一個動態(tài)方法,可以被替換综看。可被替換是指在OC運(yùn)行時的方法交換的場景下可被替換岖食。
**如果想要對Swift 方法進(jìn)行方法交換红碑,需要對被替換的方法加dynamic修飾。 **
再使用@_dynamicReplacement(for: teach)來完成替換.
示例代碼如下:
swift
class PersonClass: 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”
4. @objc dynamic 修飾的方法:消息轉(zhuǎn)發(fā)
源碼如下:
swift
@objc dynamic func changClassName9() {}
vtable SIL:
函數(shù)表中沒有changClassName9的函數(shù)析珊。
方法調(diào)度 SIL:
與普通的函數(shù)派發(fā)方法調(diào)用時不同,不是以 class_method 方式蔑穴,是以objc_method方式
運(yùn)行忠寻、匯編調(diào)試:
匯編調(diào)試時,看到了熟悉的objc_msgSend存和。這是OC的消息轉(zhuǎn)發(fā)的方式進(jìn)行方法調(diào)度奕剃。
5. static 修飾
static修飾的方法,叫做類方法捐腿,可以直接由類名去調(diào)用纵朋,無需創(chuàng)建實(shí)例對象。
源碼如下:
swift
static func changClassName11() {}
vtable SIL:
方法調(diào)度 SIL:
運(yùn)行茄袖、匯編調(diào)試:
以function_ref 的方式獲取函數(shù), 所以是靜態(tài)派發(fā)操软。
6. final 修飾
final修飾符的幾點(diǎn)使用原則
● final修飾符只能修飾類,表明該類不能被其他類繼承宪祥,也就是它沒資格當(dāng)父類聂薪。
● final修飾符也可以修飾類中的方法, 表明該方法不能被子類重寫家乘。
● final不能修飾結(jié)構(gòu)體、枚舉藏澳、協(xié)議仁锯。
源碼如下:
swift
final func changClassName1() {}
vtable SIL:
方法調(diào)度 SIL:
運(yùn)行、匯編調(diào)試:
以function_ref 的方式獲取函數(shù), 所以是靜態(tài)派發(fā)笆载。
5. 總結(jié)
函數(shù)表內(nèi)的函數(shù)扑馁,不一定是函數(shù)派發(fā)的方式去調(diào)度。但是不在函數(shù)表中的凉驻,一定不是函數(shù)派發(fā)的方式腻要。
在調(diào)用時獲取函數(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)個贊吧媳纬!
青山不改,綠水長流施掏,后會有期钮惠,感謝每一位佳人的支持!