swift class的動態派發
一、測試代碼
class BaseCallClass{
func NormalCall(){}
@objc func OcCall(){}
@objc dynamic func OcDynamicCall(){}
}
class DerivedCallClass:BaseCallClass{
override func NormalCall(){}
@objc override func OcCall(){}
@objc dynamic override func OcDynamicCall(){}
}
func FuncTest(object:BaseCallClass)
{
object.NormalCall()
object.OcCall()
object.OcDynamicCall()
}
func DoneTest(){
FuncTest(object: BaseCallClass())
FuncTest(object: DerivedCallClass())
}
二、命令行
swiftc -emit-sil DispatchCall.swift | xcrun swift-demangle > DispatchCall.silgen
cat DispatchCall.silgen
三、虛函數表
虛函數表中,函數的名稱都以:基類+函數名稱的形式定義;
同時映射到具體的函數;
sil_vtable BaseCallClass {
#BaseCallClass.NormalCall!1: (BaseCallClass) -> () -> () : @DispatchCall.BaseCallClass.NormalCall() -> () // BaseCallClass.NormalCall()
#BaseCallClass.OcCall!1: (BaseCallClass) -> () -> () : @DispatchCall.BaseCallClass.OcCall() -> () // BaseCallClass.OcCall()
}
sil_vtable DerivedCallClass {
#BaseCallClass.NormalCall!1: (BaseCallClass) -> () -> () : @DispatchCall.DerivedCallClass.NormalCall() -> () [override] // DerivedCallClass.NormalCall()
#BaseCallClass.OcCall!1: (BaseCallClass) -> () -> () : @DispatchCall.DerivedCallClass.OcCall() -> () [override] // DerivedCallClass.OcCall()
}
四、動態派發
1、調用代碼:
// FuncTest(object:)
sil hidden @DispatchCall.FuncTest(object: DispatchCall.BaseCallClass) -> () : $@convention(thin) (@guaranteed BaseCallClass) -> () {
// %0 // users: %7, %6, %5, %4, %3, %2, %1
bb0(%0 : $BaseCallClass):
debug_value %0 : $BaseCallClass, let, name "object", argno 1 // id: %1
%2 = class_method %0 : $BaseCallClass, #BaseCallClass.NormalCall!1 : (BaseCallClass) -> () -> (), $@convention(method) (@guaranteed BaseCallClass) -> () // user: %3
%3 = apply %2(%0) : $@convention(method) (@guaranteed BaseCallClass) -> ()
%4 = class_method %0 : $BaseCallClass, #BaseCallClass.OcCall!1 : (BaseCallClass) -> () -> (), $@convention(method) (@guaranteed BaseCallClass) -> () // user: %5
%5 = apply %4(%0) : $@convention(method) (@guaranteed BaseCallClass) -> ()
%6 = objc_method %0 : $BaseCallClass, #BaseCallClass.OcDynamicCall!1.foreign : (BaseCallClass) -> () -> (), $@convention(objc_method) (BaseCallClass) -> () // user: %7
%7 = apply %6(%0) : $@convention(objc_method) (BaseCallClass) -> ()
%8 = tuple () // user: %9
return %8 : $() // id: %9
} // end sil function ‘DispatchCall.FuncTest(object: DispatchCall.BaseCallClass) -> ()‘
2、虛函數表中的函數派發:
通過class_method(類的實例變量、函數名稱)的形式查找虛函數表到具體的函數;
然後apply執行;先將函數綁定到類實例,得到方法;然後調用方法執行;
3、oc的動態派發
sil提供了對swift方法的統一實現提供了兩個實現:oc可見實現和swift具體功能實現;同時將oc可見實現構造進oc的派發列表中;
派發列表的搜索和oc原生的搜索一致;先搜索子類的實現,沒有再搜索父類的實現;
提供給oc派發列表的函數是一個中間函數,這個函數與具體實現的函數一一對應,並實現了對具體函數的調用;
先通過objc_method(類的實例變量、函數名稱)查找派發列表得到chunk函數;chunk函數與函數的具體實現一一對應;
然後調用chunk函數;
chunk函數內部調用函數的具體實現;
// DerivedCallClass.OcDynamicCall()
sil hidden @DispatchCall.DerivedCallClass.OcDynamicCall() -> () : $@convention(method) (@guaranteed DerivedCallClass) -> () {
// %0 // user: %1
bb0(%0 : $DerivedCallClass):
debug_value %0 : $DerivedCallClass, let, name "self", argno 1 // id: %1
%2 = tuple () // user: %3
return %2 : $() // id: %3
} // end sil function ‘DispatchCall.DerivedCallClass.OcDynamicCall() -> ()‘
// @objc DerivedCallClass.OcDynamicCall()
sil hidden [thunk] @@objc DispatchCall.DerivedCallClass.OcDynamicCall() -> () : $@convention(objc_method) (DerivedCallClass) -> () {
// %0 // users: %4, %3, %1
bb0(%0 : $DerivedCallClass):
strong_retain %0 : $DerivedCallClass // id: %1
// function_ref DerivedCallClass.OcDynamicCall()
%2 = function_ref @DispatchCall.DerivedCallClass.OcDynamicCall() -> () : $@convention(method) (@guaranteed DerivedCallClass) -> () // user: %3
%3 = apply %2(%0) : $@convention(method) (@guaranteed DerivedCallClass) -> () // user: %5
strong_release %0 : $DerivedCallClass // id: %4
return %3 : $() // id: %5
} // end sil function ‘@objc DispatchCall.DerivedCallClass.OcDynamicCall() -> ()‘
五、第三方解釋chunk:
chunk只是包殼,功能有二:1、oc繼承體系中派發列表可見;2、消息轉發給具體的實現;
The magic bit of glue here is a thunk. In the Swift to Objective-C world, this is an additional method callable from Objective-C. It’s a thin wrapper and all it needs to do is call through to the native Swift method.
https://swiftunboxed.com/interop/objc-dynamic/
swift class的動態派發