您现在的位置是:主页 > news > 企业商城网站开发建设/文员短期电脑培训
企业商城网站开发建设/文员短期电脑培训
admin2025/6/13 18:24:41【news】
简介企业商城网站开发建设,文员短期电脑培训,wordpress整站搬家,图跃企业网站建设????????关注后回复 “进群” ,拉你进程序员交流群????????转自:收納箱https://juejin.cn/post/68875486673531371660. 引言下面的代码输出什么?(摘自objc.io twitter的swift quiz)protocol Drawing {func render() }extensi…
????????关注后回复 “进群” ,拉你进程序员交流群????????
转自:收納箱
https://juejin.cn/post/6887548667353137166
0. 引言
下面的代码输出什么?(摘自objc.io twitter的swift quiz)
protocol Drawing {func render()
}extension Drawing {func circle() { print("protocol") }func render() { circle() }
}class SVG: Drawing {func circle() { print("class") }
}SVG().render()// what's the output?
答案是:protocol 。
原因是 extension中声明的函数是静态派发,编译的时候就已经确定了调用地址,类无法重写实现。
我们通过 SIL 分析一下:
swiftc -emit-silgen -O demo.swift -o demo.sil
demo.sil
// main
sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):%2 = metatype $@thick SVG.Type // user: %4// function_ref SVG.__allocating_init()%3 = function_ref @$s4demo3SVGCACycfC : $@convention(method) (@thick SVG.Type) -> @owned SVG // user: %4%4 = apply %3(%2) : $@convention(method) (@thick SVG.Type) -> @owned SVG // user: %6%5 = alloc_stack $SVG // users: %10, %9, %8, %6store %4 to [init] %5 : $*SVG // id: %6// function_ref Drawing.render()%7 = function_ref @$s4demo7DrawingPAAE6renderyyF : $@convention(method) <τ_0_0 where τ_0_0 : Drawing> (@in_guaranteed τ_0_0) -> () // user: %8%8 = apply %7<SVG>(%5) : $@convention(method) <τ_0_0 where τ_0_0 : Drawing> (@in_guaranteed τ_0_0) -> ()destroy_addr %5 : $*SVG // id: %9dealloc_stack %5 : $*SVG // id: %10%11 = integer_literal $Builtin.Int32, 0 // user: %12%12 = struct $Int32 (%11 : $Builtin.Int32) // user: %13return %12 : $Int32 // id: %13
} // end sil function 'main'
我们可以看到SVG初始化后,是直接调用 Drawing.render() 协议的静态函数的。
// Drawing.render()
sil hidden [ossa] @$s4demo7DrawingPAAE6renderyyF : $@convention(method) <Self where Self : Drawing> (@in_guaranteed Self) -> () {
// %0 "self" // users: %3, %1
bb0(%0 : $*Self):debug_value_addr %0 : $*Self, let, name "self", argno 1 // id: %1// function_ref Drawing.circle()%2 = function_ref @$s4demo7DrawingPAAE6circleyyF : $@convention(method) <τ_0_0 where τ_0_0 : Drawing> (@in_guaranteed τ_0_0) -> () // user: %3%3 = apply %2<Self>(%0) : $@convention(method) <τ_0_0 where τ_0_0 : Drawing> (@in_guaranteed τ_0_0) -> ()%4 = tuple () // user: %5return %4 : $() // id: %5
} // end sil function '$s4demo7DrawingPAAE6renderyyF'
而对于 Drawing.render() 来说,内部也只直接调用 Drawing.circle() 的,所以这是编译期就决定了的。
了解Swift函数的派发机制,有助于你理解Swift中函数的调用过程,解决一些 “莫名其妙” 的bug。
1. 派发机制
函数派发的三种类型
静态派发
函数表派发
消息派发
1.1 静态派发
静态派发是三种派发方式中最快的。CPU 直接拿到函数地址并进行调用。编译器优化时,也常常将函数进行内联,将其转换为静态派发方式,提升执行速度。
C++ 默认使用静态派发;在 Swift 中给函数加上final关键字,也会变成静态派发。
优点:
使用最少的指令集,办最快的事情。
缺点:
静态派发最大的弊病就是没有动态性,不支持继承。
1.2 函数表派发
编译型语言中最常见的派发方式,既保证了动态性也兼顾了执行效率。
函数所在的类会维护一个“函数表”(虚函数表),存取了每个函数实现的指针。
每个类的 vtable 在编译时就会被构建,所以与静态派发相比多出了两个读取的工作:
读取该类的 vtable
读取函数的指针
优点:
查表是一种简单,易实现,而且性能可预知的方式。
理论上说,函数表派发也是一种高效的方式。
缺点:
与静态派发相比,从字节码角度来看,多了两次读和一次跳转。
与静态派发相比,编译器对某些含有副作用的函数无法优化。
Swift 类扩展里面的函数无法动态加入该类的函数表中,只能使用静态派发的方式。
举个例子(只是一个示例):
class A {func method1() {}
}
class B: A {func method2() {}
}
class C: B {override func method2() {}func method3() {}
}
offset | 0xA00 | A | 0xB00 | B | 0xC00 | C |
---|---|---|---|---|---|---|
0 | 0x121 | A.method1 | 0x121 | A.method1 | 0x121 | A.method1 |
1 | 0x222 | B.method2 | 0x322 | C.method2 | ||
2 | 0x323 | C.method3 |
let obj = C()
obj.method2()
当method2被调用时,会经历下面的几个过程:
读取对象 0xC00 的函数表
读取函数指针的索引, method2 的地址为0x322
跳转执行 0x322
1.3 消息派发
消息机制是调用函数最动态的方式。由于 Swfit 使用的依旧是 Objective-C 的运行时系统,消息派发其实也就是 Objective-C 的 Message Passing(消息传递)。由于消息传递大家看的文章很多了,这里不做过多赘述。
id returnValue = [obj messageName:param];
// 底层代码
id returnValue = objc_msgSend(obj, @selector(messageName:), param);
优点:
动态性高
Method Swizzling
isa Swizzling
...
缺点:
执行效率是三种派发方式中最低的
所幸的是 objc_msgSend
会将匹配的结果缓存到一个映射表中,每个类都有这样一块缓存。若是之后发送相同的消息,执行速率会很快。
2. Swift的派发机制
Swift的派发机制受到4个因素的影响:
数据类型
函数声明的位置
指定派发方式
编译器优化
2.1 数据类型
类型 | 初始声明 | 扩展 |
---|---|---|
值类型 | 静态派发 | 静态派发 |
协议 | 函数表派发 | 静态派发 |
类 | 函数表派发 | 静态派发 |
NSObject子类 | 函数表派发 | 静态派发 |
class MyClass {func testOfClass() {}
}struct MyStruct {func testOfStruct() {}
}
我们来看看 SIL 的结果:
class MyClass {func testOfClass()@objc deinitinit()
}struct MyStruct {func testOfStruct()init()
}// MyClass.testOfClass()
sil hidden [ossa] @$s4demo7MyClassC06testOfC0yyF : $@convention(method) (@guaranteed MyClass) -> () {
...
} // end sil function '$s4demo7MyClassC06testOfC0yyF'// MyClass.deinit
sil hidden [ossa] @$s4demo7MyClassCfd : $@convention(method) (@guaranteed MyClass) -> @owned Builtin.NativeObject {
...
} // end sil function '$s4demo7MyClassCfd'// MyClass.__deallocating_deinit
sil hidden [ossa] @$s4demo7MyClassCfD : $@convention(method) (@owned MyClass) -> () {
...
} // end sil function '$s4demo7MyClassCfD'// MyClass.__allocating_init()
sil hidden [exact_self_class] [ossa] @$s4demo7MyClassCACycfC : $@convention(method) (@thick MyClass.Type) -> @owned MyClass {
...
} // end sil function '$s4demo7MyClassCACycfC'// MyClass.init()
sil hidden [ossa] @$s4demo7MyClassCACycfc : $@convention(method) (@owned MyClass) -> @owned MyClass {
...
} // end sil function '$s4demo7MyClassCACycfc'// MyStruct.testOfStruct()
sil hidden [ossa] @$s4demo8MyStructV06testOfC0yyF : $@convention(method) (MyStruct) -> () {
...
} // end sil function '$s4demo8MyStructV06testOfC0yyF'// MyStruct.init()
sil hidden [ossa] @$s4demo8MyStructVACycfC : $@convention(method) (@thin MyStruct.Type) -> MyStruct {
...
} // end sil function '$s4demo8MyStructVACycfC'sil_vtable MyClass {#MyClass.testOfClass: (MyClass) -> () -> () : @$s4demo7MyClassC06testOfC0yyF // MyClass.testOfClass()#MyClass.init!allocator: (MyClass.Type) -> () -> MyClass : @$s4demo7MyClassCACycfC // MyClass.__allocating_init()#MyClass.deinit!deallocator: @$s4demo7MyClassCfD // MyClass.__deallocating_deinit
}
我们抛开函数具体的实现,可以看到
struct 类型仅使用静态派发,不存在 vtable 结构;
class 类型存在 vtable 结构,函数依次被存放在 vtable 中,使用函数表派发。
2.2 函数声明的位置
函数声明位置的不同也会导致派发方式的不同。
在 类 中声明
在 扩展 中声明
protocol MyProtocol {func testOfProtocol()
}extension MyProtocol {func testOfProtocolInExtension() {}
}class MyClass: MyProtocol {func testOfClass() {}func testOfProtocol() {}
}extension MyClass {func testOfClassInExtension() {}
}
我们来看看 SIL 的结果:
protocol MyProtocol {func testOfProtocol()
}extension MyProtocol {func testOfProtocolInExtension()
}class MyClass : MyProtocol {func testOfClass()func testOfProtocol()@objc deinitinit()
}extension MyClass {func testOfClassInExtension()
}// MyProtocol.testOfProtocolInExtension()
sil hidden [ossa] @$s4demo10MyProtocolPAAE06testOfC11InExtensionyyF : $@convention(method) <Self where Self : MyProtocol> (@in_guaranteed Self) -> () {
...
} // end sil function '$s4demo10MyProtocolPAAE06testOfC11InExtensionyyF'// MyClass.testOfClass()
sil hidden [ossa] @$s4demo7MyClassC06testOfC0yyF : $@convention(method) (@guaranteed MyClass) -> () {
...
} // end sil function '$s4demo7MyClassC06testOfC0yyF'// MyClass.testOfProtocol()
sil hidden [ossa] @$s4demo7MyClassC14testOfProtocolyyF : $@convention(method) (@guaranteed MyClass) -> () {
...
} // end sil function '$s4demo7MyClassC14testOfProtocolyyF'// MyClass.deinit
sil hidden [ossa] @$s4demo7MyClassCfd : $@convention(method) (@guaranteed MyClass) -> @owned Builtin.NativeObject {
...
} // end sil function '$s4demo7MyClassCfd'// MyClass.__deallocating_deinit
sil hidden [ossa] @$s4demo7MyClassCfD : $@convention(method) (@owned MyClass) -> () {
...
} // end sil function '$s4demo7MyClassCfD'// MyClass.__allocating_init()
sil hidden [exact_self_class] [ossa] @$s4demo7MyClassCACycfC : $@convention(method) (@thick MyClass.Type) -> @owned MyClass {
...
} // end sil function '$s4demo7MyClassCACycfC'// MyClass.init()
sil hidden [ossa] @$s4demo7MyClassCACycfc : $@convention(method) (@owned MyClass) -> @owned MyClass {
...
} // end sil function '$s4demo7MyClassCACycfc'// protocol witness for MyProtocol.testOfProtocol() in conformance MyClass
sil private [transparent] [thunk] [ossa] @$s4demo7MyClassCAA0B8ProtocolA2aDP06testOfD0yyFTW : $@convention(witness_method: MyProtocol) (@in_guaranteed MyClass) -> () {
...
} // end sil function '$s4demo7MyClassCAA0B8ProtocolA2aDP06testOfD0yyFTW'// MyClass.testOfClassInExtension()
sil hidden [ossa] @$s4demo7MyClassC06testOfC11InExtensionyyF : $@convention(method) (@guaranteed MyClass) -> () {
...
} // end sil function '$s4demo7MyClassC06testOfC11InExtensionyyF'sil_vtable MyClass {#MyClass.testOfClass: (MyClass) -> () -> () : @$s4demo7MyClassC06testOfC0yyF // MyClass.testOfClass()#MyClass.testOfProtocol: (MyClass) -> () -> () : @$s4demo7MyClassC14testOfProtocolyyF // MyClass.testOfProtocol()#MyClass.init!allocator: (MyClass.Type) -> () -> MyClass : @$s4demo7MyClassCACycfC // MyClass.__allocating_init()#MyClass.deinit!deallocator: @$s4demo7MyClassCfD // MyClass.__deallocating_deinit
}sil_witness_table hidden MyClass: MyProtocol module demo {method #MyProtocol.testOfProtocol: <Self where Self : MyProtocol> (Self) -> () -> () : @$s4demo7MyClassCAA0B8ProtocolA2aDP06testOfD0yyFTW // protocol witness for MyProtocol.testOfProtocol() in conformance MyClass
}
我们抛开函数具体的实现,可以看到
声明在 协议 或者 类 中的函数是使用函数表派发的
声明在 扩展 中的函数则是静态派发
此外,我们可以看到,MyClass 实现 MyProtocol 的 testOfProtocol 在 sil_witness_table 中的函数地址对应的实现。
// protocol witness for MyProtocol.testOfProtocol() in conformance MyClass
sil private [transparent] [thunk] [ossa] @$s4demo7MyClassCAA0B8ProtocolA2aDP06testOfD0yyFTW : $@convention(witness_method: MyProtocol) (@in_guaranteed MyClass) -> () {
// %0 // user: %1
bb0(%0 : $*MyClass):%1 = load_borrow %0 : $*MyClass // users: %5, %3, %2%2 = class_method %1 : $MyClass, #MyClass.testOfProtocol : (MyClass) -> () -> (), $@convention(method) (@guaranteed MyClass) -> () // user: %3%3 = apply %2(%1) : $@convention(method) (@guaranteed MyClass) -> ()%4 = tuple () // user: %6end_borrow %1 : $MyClass // id: %5return %4 : $() // id: %6
} // end sil function '$s4demo7MyClassCAA0B8ProtocolA2aDP06testOfD0yyFTW'sil_witness_table hidden MyClass: MyProtocol module demo {method #MyProtocol.testOfProtocol: <Self where Self : MyProtocol> (Self) -> () -> () : @$s4demo7MyClassCAA0B8ProtocolA2aDP06testOfD0yyFTW // protocol witness for MyProtocol.testOfProtocol() in conformance MyClass
}
可以看到,通过 testOfProtocol 的具体实现 @$s4demo7MyClassCAA0B8ProtocolA2aDP06testOfD0yyFTW 我们可以看到,在其内部还是执行的MyClass的 MyClass.testOfProtocol 函数。
即,无论是通过协议,还是通过类进行访问,最终都访问的是 MyClass.testOfProtocol 函数。
2.3 指定派发方式
给函数添加关键字的修饰也会改变其派发方式。
final
添加了 final 关键字的函数无法被重写,使用静态派发,不会在 vtable 中出现,且对 objc 运行时不可见。
class Test {final func foo() {}
}
Test().foo()sil_vtable Test {#Test.init!allocator: (Test.Type) -> () -> Test : @$s4demo4TestCACycfC // Test.__allocating_init()#Test.deinit!deallocator: @$s4demo4TestCfD // Test.__deallocating_deinit
}// main
sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):%2 = metatype $@thick Test.Type // user: %4// function_ref Test.__allocating_init()%3 = function_ref @$s4demo4TestCACycfC : $@convention(method) (@thick Test.Type) -> @owned Test // user: %4%4 = apply %3(%2) : $@convention(method) (@thick Test.Type) -> @owned Test // users: %7, %6// function_ref Test.foo()%5 = function_ref @$s4demo4TestC3fooyyF : $@convention(method) (@guaranteed Test) -> () // user: %6%6 = apply %5(%4) : $@convention(method) (@guaranteed Test) -> ()destroy_value %4 : $Test // id: %7%8 = integer_literal $Builtin.Int32, 0 // user: %9%9 = struct $Int32 (%8 : $Builtin.Int32) // user: %10return %9 : $Int32 // id: %10
} // end sil function 'main'
final 关键字会将函数变为静态派发,不会在 vtable 中出现。从 main 函数中的调用 function_ref 也可以看得出。
dynamic
函数均可添加 dynamic 关键字,为非objc类和值类型的函数赋予动态性,但派发方式还是函数表派发。
下面这个例子展示了如何利用 dynamic 关键字,实现 Method Swizzling 。
class Test {dynamic func foo() {print("bar")}
}extension Test {@_dynamicReplacement(for: foo())func foo_new() {print("bar new")}
}Test().foo() // bar new
@objc
该关键字可以将Swift函数暴露给Objc运行时,但并不会改变其派发方式,依旧是函数表派发。
class Test {@objc func foo() {}
}// Test.foo()
sil hidden [ossa] @$s4demo4TestC3fooyyF : $@convention(method) (@guaranteed Test) -> () {
// %0 "self" // user: %1
bb0(%0 : @guaranteed $Test):debug_value %0 : $Test, let, name "self", argno 1 // id: %1%2 = tuple () // user: %3return %2 : $() // id: %3
} // end sil function '$s4demo4TestC3fooyyF'// @objc Test.foo()
sil hidden [thunk] [ossa] @$s4demo4TestC3fooyyFTo : $@convention(objc_method) (Test) -> () {
// %0 // user: %1
bb0(%0 : @unowned $Test):%1 = copy_value %0 : $Test // users: %6, %2%2 = begin_borrow %1 : $Test // users: %5, %4// function_ref Test.foo()%3 = function_ref @$s4demo4TestC3fooyyF : $@convention(method) (@guaranteed Test) -> () // user: %4%4 = apply %3(%2) : $@convention(method) (@guaranteed Test) -> () // user: %7end_borrow %2 : $Test // id: %5destroy_value %1 : $Test // id: %6return %4 : $() // id: %7
} // end sil function '$s4demo4TestC3fooyyFTo'...sil_vtable Test {#Test.foo: (Test) -> () -> () : @$s4demo4TestC3fooyyF // Test.foo()#Test.init!allocator: (Test.Type) -> () -> Test : @$s4demo4TestCACycfC // Test.__allocating_init()#Test.deinit!deallocator: @$s4demo4TestCfD // Test.__deallocating_deinit
}
foo 函数依然在 vtable 中,暴露给 Objec 的函数 @objc Test.foo() 其内部也是调用的 foo 函数。
@objc + dynamic
class Test {dynamic func foo1() {}@objc func foo2() {}@objc dynamic func foo3() {}
}let text = Test()
text.foo1()
text.foo2()
text.foo3()sil_vtable Test {#Test.foo1: (Test) -> () -> () : @$s4demo4TestC4foo1yyF // Test.foo1()#Test.foo2: (Test) -> () -> () : @$s4demo4TestC4foo2yyF // Test.foo2()#Test.init!allocator: (Test.Type) -> () -> Test : @$s4demo4TestCACycfC // Test.__allocating_init()#Test.deinit!deallocator: @$s4demo4TestCfD // Test.__deallocating_deinit
}
vtable 中只有 foo1 和 foo2 ,没有 foo3。
// main
sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):alloc_global @$s4demo4textAA4TestCvp // id: %2%3 = global_addr @$s4demo4textAA4TestCvp : $*Test // users: %16, %12, %8, %7%4 = metatype $@thick Test.Type // user: %6// function_ref Test.__allocating_init()%5 = function_ref @$s4demo4TestCACycfC : $@convention(method) (@thick Test.Type) -> @owned Test // user: %6%6 = apply %5(%4) : $@convention(method) (@thick Test.Type) -> @owned Test // user: %7store %6 to [init] %3 : $*Test // id: %7%8 = load_borrow %3 : $*Test // users: %11, %10, %9%9 = class_method %8 : $Test, #Test.foo1 : (Test) -> () -> (), $@convention(method) (@guaranteed Test) -> () // user: %10%10 = apply %9(%8) : $@convention(method) (@guaranteed Test) -> ()end_borrow %8 : $Test // id: %11%12 = load_borrow %3 : $*Test // users: %15, %14, %13%13 = class_method %12 : $Test, #Test.foo2 : (Test) -> () -> (), $@convention(method) (@guaranteed Test) -> () // user: %14%14 = apply %13(%12) : $@convention(method) (@guaranteed Test) -> ()end_borrow %12 : $Test // id: %15%16 = load_borrow %3 : $*Test // users: %19, %18, %17%17 = objc_method %16 : $Test, #Test.foo3!foreign : (Test) -> () -> (), $@convention(objc_method) (Test) -> () // user: %18%18 = apply %17(%16) : $@convention(objc_method) (Test) -> ()end_borrow %16 : $Test // id: %19%20 = integer_literal $Builtin.Int32, 0 // user: %21%21 = struct $Int32 (%20 : $Builtin.Int32) // user: %22return %21 : $Int32 // id: %22
} // end sil function 'main'
从 main 函数的调用来看,Test.foo1 和 Test.foo2 都是通过 class_method 采用 函数表 的方式。而 Test.foo3 则是通过 objc_method 采用 消息派发 的方式。
@inline
class Test {@inline(__always) func foo() {}
}sil_vtable Test {#Test.foo: (Test) -> () -> () : @$s4demo4TestC3fooyyF // Test.foo()#Test.init!allocator: (Test.Type) -> () -> Test : @$s4demo4TestCACycfC // Test.__allocating_init()#Test.deinit!deallocator: @$s4demo4TestCfD // Test.__deallocating_deinit
}
告诉编译器将此函数静态派发,但将其转换成SIL代码后,依旧是 vtable 派发。
static
class Test {static func foo() {}
}
Test.foo()sil_vtable Test {#Test.init!allocator: (Test.Type) -> () -> Test : @$s4demo4TestCACycfC // Test.__allocating_init()#Test.deinit!deallocator: @$s4demo4TestCfD // Test.__deallocating_deinit
}// main
sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):%2 = metatype $@thick Test.Type // user: %4// function_ref static Test.foo()%3 = function_ref @$s4demo4TestC3fooyyFZ : $@convention(method) (@thick Test.Type) -> () // user: %4%4 = apply %3(%2) : $@convention(method) (@thick Test.Type) -> ()%5 = integer_literal $Builtin.Int32, 0 // user: %6%6 = struct $Int32 (%5 : $Builtin.Int32) // user: %7return %6 : $Int32 // id: %7
} // end sil function 'main'
static 关键字会将函数变为静态派发,不会在 vtable 中出现。从 main 函数中的调用 function_ref 也可以看得出。
总结
类型 | 静态派发 | 函数表派发 | 消息派发 |
---|---|---|---|
值类型 | 所有方法 | / | / |
协议 | extension | 主体创建 | / |
类 | extension/final/static | 主体创建 | @objc + dynamic |
NSObject子类 | extension/final/static | 主体创建 | @objc + dynamic |
除此之外,编译器可能将某些方法优化为静态派发。例如,私有函数。
2.4 编译器优化
Swift 会尽可能的去优化函数派发方式。当一个类声明了一个私有函数时,该函数很可能会被优化为静态派发。
这也就是为什么在 Swift 中使用 target-action 模式时,私有 selector 会报错的原因(Objective-C 无法获取 #selector 指定的函数)。
另一个需要注意的是,NSObject子类 中的 属性,如果没有使用 dynamic 修饰的话, 这个优化会默认让 KVO 失效。因为,这个属性的 getter 和 setter 会被优化为静态派发。虽然,代码可以通过编译,不过动态生成的 KVO 函数就不会被触发。
3. 面试题2
下面的代码输出什么?
protocol Logger {func log(content: String)
}extension Logger{func log(content: String){print(content)}func description()->String{return "Logger"}
}class MyLogger:Logger{func log(content: String) {print("MyLogger: " + content)}func description()->String{return "MyLogger"}
}let p1:Logger = MyLogger()
p1.log(content: "p1")
print(p1.description())let p2:MyLogger = MyLogger()
p2.log(content: "p2")
print(p2.description())
答案:
let p1:Logger = MyLogger()
p1.log(content: "p1") // MyLogger: p1
print(p1.description()) // Loggerlet p2:MyLogger = MyLogger()
p2.log(content: "p2") // MyLogger: p2
print(p2.description()) // MyLogger
为什么会产生这种结果呢?
sil_vtable MyLogger {#MyLogger.log: (MyLogger) -> (String) -> () : @$s4demo8MyLoggerC3log7contentySS_tF // MyLogger.log(content:)#MyLogger.description: (MyLogger) -> () -> String : @$s4demo8MyLoggerC11descriptionSSyF // MyLogger.description()#MyLogger.init!allocator: (MyLogger.Type) -> () -> MyLogger : @$s4demo8MyLoggerCACycfC // MyLogger.__allocating_init()#MyLogger.deinit!deallocator: @$s4demo8MyLoggerCfD // MyLogger.__deallocating_deinit
}sil_witness_table hidden MyLogger: Logger module demo {method #Logger.log: <Self where Self : Logger> (Self) -> (String) -> () : @$s4demo8MyLoggerCAA0C0A2aDP3log7contentySS_tFTW // protocol witness for Logger.log(content:) in conformance MyLogger
}/* ⚠️**重点** */
// protocol witness for Logger.log(content:) in conformance MyLogger
sil private [transparent] [thunk] [ossa] @$s4demo8MyLoggerCAA0C0A2aDP3log7contentySS_tFTW : $@convention(witness_method: Logger) (@guaranteed String, @in_guaranteed MyLogger) -> () {
// %0 // user: %4
// %1 // user: %2
bb0(%0 : @guaranteed $String, %1 : $*MyLogger):%2 = load_borrow %1 : $*MyLogger // users: %6, %4, %3%3 = class_method %2 : $MyLogger, #MyLogger.log : (MyLogger) -> (String) -> (), $@convention(method) (@guaranteed String, @guaranteed MyLogger) -> () // user: %4%4 = apply %3(%0, %2) : $@convention(method) (@guaranteed String, @guaranteed MyLogger) -> ()%5 = tuple () // user: %7end_borrow %2 : $MyLogger // id: %6return %5 : $() // id: %7
} // end sil function '$s4demo8MyLoggerCAA0C0A2aDP3log7contentySS_tFTW'
由于 Swift 是强类型的语言,所以 p1 为 Logger 类型, p2 为 MyLogger 类型。
protocol 通过 sil_witness_table 进行调用。sil_witness_table 中的 Logger.log 函数,内部实现为通过 class_method 调用 MyLogger.log ,所以无论是 p1 还是 p2 均正常输出 MyLogger: p1/p2 。
而对于 description 函数会有一些不同:
对于 p1 Logger 类型来说,这是一个静态派发的函数,所以输出 Logger;
对于 p2 MyLogger 类型来说,这是一个 vtabel 派发的函数,所以输出 MyLogger。
分析 main 函数,也可以得出相同的结论:
// main
sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {...// function_ref Logger.description()%26 = function_ref @$s4demo6LoggerPAAE11descriptionSSyF : $@convention(method) <τ_0_0 where τ_0_0 : Logger> (@in_guaranteed τ_0_0) -> @owned String // user: %27...%63 = class_method %62 : $MyLogger, #MyLogger.description : (MyLogger) -> () -> String,
} // end sil function 'main'
对于 p1 Logger 类型来说,通过 function_ref 进行调用,为静态派发;
对于 p2 MyLogger 类型来说,通过 class_method 进行调用,为函数表派发。
-End-
最近有一些小伙伴,让我帮忙找一些 面试题 资料,于是我翻遍了收藏的 5T 资料后,汇总整理出来,可以说是程序员面试必备!所有资料都整理到网盘了,欢迎下载!
点击????卡片,关注后回复【面试题
】即可获取
在看点这里好文分享给更多人↓↓