Objective-C最大的特性無疑是其的動態(tài)性,可以利用OC的動態(tài)性能夠獲得一個類的方法和屬性,從而實現(xiàn)靈活的程序,但Swift是否也包含了runtime機制呢?
下面我們將從純Swift的類和繼承OC的Swift類來闡述Swift的runtime機制。
用例分析:
1、獲取類的方法,屬性
Swift的類:TestASwithClass
Objective-C的類:TestSwiftClass繼承UIViewController繼承NSObject
動態(tài)性最重要的一點就是拿到某個類的方法和屬性,使用如下的方法打印類的方法和屬性
提供出來測試TestASwithClass著瓶、TestSwiftClass類的測試函數(shù)(方法):
調(diào)用showClsRuntime打印方法
打印如下:
結(jié)果分析:
對于純Swift的TestASwithClass來說任何方法漂彤、屬性都未獲取到。
對于TestSwiftClass來說除testReturnTuple梢什、testReturnVoidWithaCharacter兩個方法外,其他的都獲取成功了。
這是為什么呢?
1:純Swift類的函數(shù)調(diào)用已經(jīng)不是OC那樣的運行時消息了,而是類似C++似得vtable,在編譯時就確定了調(diào)用那個函數(shù)了壶笼。
2:而TestSwiftClass繼承自UIViewController也就是NSObject,Swift為了兼容OC,所以繼承自NSObject的類都保留了他的動態(tài)性,所以我們能通過runtime拿到他的屬性和方法。
可是為什么testReturnTuple雁刷、testReturnVoidWithaCharacter這兩個函數(shù)卻無法通過runtime獲得呢?
從OC的動態(tài)特性可知,所有運行時方法都依賴TypeEcoding,也就是method_getTypeEncoding函數(shù),它指定了參數(shù)類型以及參數(shù)在入棧時的內(nèi)存空間,沒有這個標識則沒法入棧.而元祖,和字符類型是Swift獨有的,所以不能利用runtime獲得他的方法覆劈。
2、方法替代
動態(tài)性最常用的方法就是方法替代,將某個類的方法替代為自定義的方法,從而起到hook的作用沛励。
對于純Swift類(如TestASwithClass)來說责语,無法通過objc runtime替換方法,因為由上面的測試可知拿不到這些方法目派、屬性
對于繼承自NSObject類(如TestSwiftVC)來說坤候,無法通過runtime獲取到的方法肯定沒法替換了。那能通過runtime獲取到的方法就都能被替換嗎企蹭?我們測一把
Method Swizzling的代碼如下:
找到官方文檔讀讀白筹。
@objc
可以知道@objc是用來將Swift的API導出給Objective-C和Objective-C runtime使用的智末,如果你的類繼承自O(shè)bjective-c的類(如NSObject)將會自動被編譯器插入@objc標識。
我們在把TestASwiftClass(純Swift類)的方法徒河、屬性前都加個@objc 試試
文檔里還有一句說明:
dynamic
加了@objc標識的方法系馆、屬性無法保證都會被運行時調(diào)用,因為Swift會做靜態(tài)優(yōu)化虚青。要想完全被動態(tài)調(diào)用它呀,必須使用dynamic修飾。使用dynamic修飾將會隱式的加上@objc標識棒厘。
這也就解釋了為什么testReturnVoidWithaId無法被替換纵穿,因為寫在Swift里的代碼直接被編譯優(yōu)化成靜態(tài)調(diào)用了。
而viewDidAppear是繼承Objective-C類獲得的方法奢人,本身就被修飾為dynamic谓媒,所以能被動態(tài)替換。