Which dispatch method would be used in Swift?-Existential Container
In this example:
protocol MyProtocol {
func testFuncA()
}
extension MyProtocol {
func testFuncA() {
print("MyProtocol's testFuncA")
}
}
class MyClass : MyProtocol {}
let object: MyClass = MyClass()
object.testFuncA()
static dispatch is used. The concrete type of object is known at compile time; it's MyClass. Swift can then see that it conforms to MyProtocol without providing its own implementation of testFuncA(), so it can dispatch straight to the extension method.
So to answer your individual questions:
MyClassMyClassNo – a Swift class v-table only holds methods defined in the body of the class declaration. That is to say:
protocol MyProtocol {
func testFuncA()
}
extension MyProtocol {
// No entry in MyClass' Swift v-table.
// (but an entry in MyClass' protocol witness table for conformance to MyProtocol)
func testFuncA() {
print("MyProtocol's testFuncA")
}
}
class MyClass : MyProtocol {
// An entry in MyClass' Swift v-table.
func foo() {}
}
extension MyClass {
// No entry in MyClass' Swift v-table (this is why you can't override
// extension methods without using Obj-C message dispatch).
func bar() {}
}
There are no existential containers in the code:
let object: MyClass = MyClass()
object.testFuncA()
Existential containers are used for protocol-typed instances, such as your first example:
let object: MyProtocol = MyClass()
object.testFuncA()
The MyClass instance is boxed in an existential container with a protocol witness table that maps calls to testFuncA() to the extension method (now we're dealing with dynamic dispatch).
A nice way to see all of the above in action is by taking a look at the SIL generated by the compiler; which is a fairly high-level intermediate representation of the generated code (but low-level enough to see what kind of dispatch mechanisms are in play).
You can do so by running the following (note it's best to first remove print statements from your program, as they inflate the size of the SIL generated considerably):
swiftc -emit-sil main.swift | xcrun swift-demangle > main.silgen
Let's take a look at the SIL for the first example in this answer. Here's the main function, which is the entry-point of the program:
// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
alloc_global @main.object : main.MyClass // id: %2
%3 = global_addr @main.object : main.MyClass : $*MyClass // users: %9, %7
// function_ref MyClass.__allocating_init()
%4 = function_ref @main.MyClass.__allocating_init() -> main.MyClass : $@convention(method) (@thick MyClass.Type) -> @owned MyClass // user: %6
%5 = metatype $@thick MyClass.Type // user: %6
%6 = apply %4(%5) : $@convention(method) (@thick MyClass.Type) -> @owned MyClass // user: %7
store %6 to %3 : $*MyClass // id: %7
// Get a reference to the extension method and call it (static dispatch).
// function_ref MyProtocol.testFuncA()
%8 = function_ref @(extension in main):main.MyProtocol.testFuncA() -> () : $@convention(method) <τ_0_0 where τ_0_0 : MyProtocol> (@in_guaranteed τ_0_0) -> () // user: %12
%9 = load %3 : $*MyClass // user: %11
%10 = alloc_stack $MyClass // users: %11, %13, %12
store %9 to %10 : $*MyClass // id: %11
%12 = apply %8<MyClass>(%10) : $@convention(method) <τ_0_0 where τ_0_0 : MyProtocol> (@in_guaranteed τ_0_0) -> ()
dealloc_stack %10 : $*MyClass // id: %13
%14 = integer_literal $Builtin.Int32, 0 // user: %15
%15 = struct $Int32 (%14 : $Builtin.Int32) // user: %16
return %15 : $Int32 // id: %16
} // end sil function 'main'
The bit that we're interested in here is this line:
%8 = function_ref @(extension in main):main.MyProtocol.testFuncA() -> () : $@convention(method) <τ_0_0 where τ_0_0 : MyProtocol> (@in_guaranteed τ_0_0) -> () // user: %12
The function_ref instruction gets a reference to a function known at compile-time. You can see that it's getting a reference to the function @(extension in main):main.MyProtocol.testFuncA() -> (), which is the method in the protocol extension. Thus Swift is using static dispatch.
Let's now take a look at what happens when we make the call like this:
let object: MyProtocol = MyClass()
object.testFuncA()
The main function now looks like this:
// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
alloc_global @main.object : main.MyProtocol // id: %2
%3 = global_addr @main.object : main.MyProtocol : $*MyProtocol // users: %9, %4
// Create an opaque existential container and get its address (%4).
%4 = init_existential_addr %3 : $*MyProtocol, $MyClass // user: %8
// function_ref MyClass.__allocating_init()
%5 = function_ref @main.MyClass.__allocating_init() -> main.MyClass : $@convention(method) (@thick MyClass.Type) -> @owned MyClass // user: %7
%6 = metatype $@thick MyClass.Type // user: %7
%7 = apply %5(%6) : $@convention(method) (@thick MyClass.Type) -> @owned MyClass // user: %8
// Store the MyClass instance in the existential container.
store %7 to %4 : $*MyClass // id: %8
// Open the existential container to get a pointer to the MyClass instance.
%9 = open_existential_addr immutable_access %3 : $*MyProtocol to $*@opened("F199B87A-06BA-11E8-A29C-DCA9047B1400") MyProtocol // users: %11, %11, %10
// Dynamically lookup the function to call for the testFuncA requirement.
%10 = witness_method $@opened("F199B87A-06BA-11E8-A29C-DCA9047B1400") MyProtocol, #MyProtocol.testFuncA!1 : <Self where Self : MyProtocol> (Self) -> () -> (), %9 : $*@opened("F199B87A-06BA-11E8-A29C-DCA9047B1400") MyProtocol : $@convention(witness_method) <τ_0_0 where τ_0_0 : MyProtocol> (@in_guaranteed τ_0_0) -> () // type-defs: %9; user: %11
// Call the function we looked-up for the testFuncA requirement.
%11 = apply %10<@opened("F199B87A-06BA-11E8-A29C-DCA9047B1400") MyProtocol>(%9) : $@convention(witness_method) <τ_0_0 where τ_0_0 : MyProtocol> (@in_guaranteed τ_0_0) -> () // type-defs: %9
%12 = integer_literal $Builtin.Int32, 0 // user: %13
%13 = struct $Int32 (%12 : $Builtin.Int32) // user: %14
return %13 : $Int32 // id: %14
} // end sil function 'main'
There are some key differences here.
An (opaque) existential container is created with init_existential_addr, and the MyClass instance is stored into it (store %7 to %4).
The existential container is then opened with open_existential_addr, which gets a pointer to the instance stored (the MyClass instance).
Then, witness_method is used in order to lookup the function to call for the protocol requirement MyProtocol.testFuncA for the MyClass instance. This will check the protocol witness table for MyClass's conformance, which is listed at the bottom of the generated SIL:
sil_witness_table hidden MyClass: MyProtocol module main {
method #MyProtocol.testFuncA!1: <Self where Self : MyProtocol> (Self) -> () -> () : @protocol witness for main.MyProtocol.testFuncA() -> () in conformance main.MyClass : main.MyProtocol in main // protocol witness for MyProtocol.testFuncA() in conformance MyClass
}
This lists the function @protocol witness for main.MyProtocol.testFuncA() -> (). We can check the implementation of this function:
// protocol witness for MyProtocol.testFuncA() in conformance MyClass
sil private [transparent] [thunk] @protocol witness for main.MyProtocol.testFuncA() -> () in conformance main.MyClass : main.MyProtocol in main : $@convention(witness_method) (@in_guaranteed MyClass) -> () {
// %0 // user: %2
bb0(%0 : $*MyClass):
%1 = alloc_stack $MyClass // users: %7, %6, %4, %2
copy_addr %0 to [initialization] %1 : $*MyClass // id: %2
// Get a reference to the extension method and call it.
// function_ref MyProtocol.testFuncA()
%3 = function_ref @(extension in main):main.MyProtocol.testFuncA() -> () : $@convention(method) <τ_0_0 where τ_0_0 : MyProtocol> (@in_guaranteed τ_0_0) -> () // user: %4
%4 = apply %3<MyClass>(%1) : $@convention(method) <τ_0_0 where τ_0_0 : MyProtocol> (@in_guaranteed τ_0_0) -> ()
%5 = tuple () // user: %8
destroy_addr %1 : $*MyClass // id: %6
dealloc_stack %1 : $*MyClass // id: %7
return %5 : $() // id: %8
} // end sil function 'protocol witness for main.MyProtocol.testFuncA() -> () in conformance main.MyClass : main.MyProtocol in main'
and sure enough, its getting a function_ref to the extension method, and calling that function.
The looked-up witness function is then called after the witness_method lookup with the line:
%11 = apply %10<@opened("F199B87A-06BA-11E8-A29C-DCA9047B1400") MyProtocol>(%9) : $@convention(witness_method) <τ_0_0 where τ_0_0 : MyProtocol> (@in_guaranteed τ_0_0) -> () // type-defs: %9
So, we can conclude that dynamic protocol dispatch is used here, based on the use of witness_method.
We just breezed though quite a lot of technical details here; feel free to work through the SIL line-by-line, using the documentation to find out what each instruction does. I'm happy to clarify anything you may be unsure about.
https://stackoverflow.com/questions/48422621/which-dispatch-method-would-be-used-in-swift
Which dispatch method would be used in Swift?-Existential Container的更多相关文章
- Which dispatch method would be used in Swift?
In this example: protocol MyProtocol { func testFuncA() } extension MyProtocol { func testFuncA() { ...
- 通过设置swift中container的ACL提供匿名访问及用户授权读取服务
在上层使用swift提供的云存储服务的过程中,提出了无需验证的使用需求. 在参考了:http://my.oschina.net/alanlqc/blog/160196(curl命令操作) 官方文档: ...
- swift 该死的派发机制--待完成
swift 该死的派发机制 final static oc类型 多态类型 静态类型 动态函数 静态函数 nsobject: 1.缺省不再使用oc的动态派发机制: 2.可以使用nsobject暴露出来 ...
- 【基本功】深入剖析Swift性能优化
简介 2014年,苹果公司在WWDC上发布Swift这一新的编程语言.经过几年的发展,Swift已经成为iOS开发语言的“中流砥柱”,Swift提供了非常灵活的高级别特性,例如协议.闭包.泛型等,并且 ...
- 深入剖析Swift性能优化
简介 2014年,苹果公司在WWDC上发布Swift这一新的编程语言.经过几年的发展,Swift已经成为iOS开发语言的“中流砥柱”,Swift提供了非常灵活的高级别特性,例如协议.闭包.泛型等,并且 ...
- Swift 性能相关
起初的疑问源自于「在 Swift 中的, Struct:Protocol 比 抽象类 好在哪里?」.但是找来找去都是 Swift 性能相关的东西.整理了点笔记,供大家可以参考一下. 一些疑问 在正题开 ...
- Swift进阶之内存模型和方法调度
前言 Apple今年推出了Swift3.0,较2.3来说,3.0是一次重大的升级.关于这次更新,在这里都可以找到,最主要的还是提高了Swift的性能,优化了Swift API的设计(命名)规范. 前段 ...
- [转] How to dispatch a Redux action with a timeout?
How to dispatch a Redux action with a timeout? Q I have an action that updates notification state of ...
- Using Swift with Cocoa and Objective-C(Swift 2.0版):开始--基础设置-备
这是一个正在研发的API或技术的概要文件,苹果公司提供这些信息主要是为了帮助你通过苹果产品使用这些技术或者编程接口而做好计划,该信息有可能会在未来发生改变,本文当中提到的软件应该以最终发布的操作系统测 ...
随机推荐
- CentOS 7.2更改网卡名称
背景 没啥背景,就是VMWare装的CentOS虚拟机的自带网卡名有点乱,想重新定义一下. 环境 1.VMWare虚拟机 6张网卡 2.系统 [root@localhost ~]# cat /etc/ ...
- Linux MTD下获取Nand flash各个参数的过程的详细解析【转】
本文转载自:https://www.crifan.com/files/doc/docbook/nand_get_type/release/html/nand_get_type.html 文章不错可以看 ...
- Linux 杀死所有进程
方法一: sudo killall -9 netease-cloud-music 这种方法,必须要写全称. sudo netease-cloud-music QStandardPaths: XDG_R ...
- 深入探析c# Socket
最近浏览了几篇有关Socket发送消息的文章,发现大家对Socket Send方法理解有所偏差,现将自己在开发过程中对Socket的领悟写出来,以供大家参考. (一)架构 基于TCP协议的Socket ...
- Masonry 动画更新约束
前言 说到iOS自动布局,有很多的解决办法.有的人使用xib/storyboard自动布局,也有人使用frame来适配.对于前者,笔者并不喜欢,也不支持.对于后者,更是麻烦,到处计算高度.宽度等,千万 ...
- 初识Spring Boot框架和快速入门
前面的铺垫文章已经连着写了六篇了,主要是介绍了spring和SpringMVC框架,小伙伴们在学习的过程中大概也发现了这两个框架需要我们手动配置的地方非常多,不过做JavaEE开发的小伙伴们肯定也听说 ...
- 无参数的lambda匿名函数
lambda 语法: lambda [arg1[,arg2,arg3....argN]]:expression 1.单个参数的: g = lambda x:x*2 print g(3) 结果是6 2. ...
- 计算属性computed 与methods
你可能已经注意到我们可以通过调用表达式中的 method 来达到同样的效果: <p>Reversed message: "{{ reversedMessage() }}" ...
- UI:网络请求
JSON 外层是一个数组或者字典 富文本(相对来说比较安全).超文本,https安全超文本协议 NSURL NSURL *url = [[NSURL alloc]initWithString:@&qu ...
- 异常处理:Cannot attach the file as database 'membership'.
Cannot attach the file 'D:\GitHome\cae\CAE\App_Data\membership.mdf' as database 'membership'. 说明: 执行 ...