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的更多相关文章

  1. Which dispatch method would be used in Swift?

    In this example: protocol MyProtocol { func testFuncA() } extension MyProtocol { func testFuncA() { ...

  2. 通过设置swift中container的ACL提供匿名访问及用户授权读取服务

    在上层使用swift提供的云存储服务的过程中,提出了无需验证的使用需求. 在参考了:http://my.oschina.net/alanlqc/blog/160196(curl命令操作) 官方文档: ...

  3. swift 该死的派发机制--待完成

    swift 该死的派发机制 final static oc类型 多态类型 静态类型 动态函数  静态函数 nsobject: 1.缺省不再使用oc的动态派发机制: 2.可以使用nsobject暴露出来 ...

  4. 【基本功】深入剖析Swift性能优化

    简介 2014年,苹果公司在WWDC上发布Swift这一新的编程语言.经过几年的发展,Swift已经成为iOS开发语言的“中流砥柱”,Swift提供了非常灵活的高级别特性,例如协议.闭包.泛型等,并且 ...

  5. 深入剖析Swift性能优化

    简介 2014年,苹果公司在WWDC上发布Swift这一新的编程语言.经过几年的发展,Swift已经成为iOS开发语言的“中流砥柱”,Swift提供了非常灵活的高级别特性,例如协议.闭包.泛型等,并且 ...

  6. Swift 性能相关

    起初的疑问源自于「在 Swift 中的, Struct:Protocol 比 抽象类 好在哪里?」.但是找来找去都是 Swift 性能相关的东西.整理了点笔记,供大家可以参考一下. 一些疑问 在正题开 ...

  7. Swift进阶之内存模型和方法调度

    前言 Apple今年推出了Swift3.0,较2.3来说,3.0是一次重大的升级.关于这次更新,在这里都可以找到,最主要的还是提高了Swift的性能,优化了Swift API的设计(命名)规范. 前段 ...

  8. [转] 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 ...

  9. Using Swift with Cocoa and Objective-C(Swift 2.0版):开始--基础设置-备

    这是一个正在研发的API或技术的概要文件,苹果公司提供这些信息主要是为了帮助你通过苹果产品使用这些技术或者编程接口而做好计划,该信息有可能会在未来发生改变,本文当中提到的软件应该以最终发布的操作系统测 ...

随机推荐

  1. 网页音乐播放器javascript实现,可以显示歌词

    可以显示歌词,但是歌词和歌曲都要实现自己下载下来.只能播放一首歌,歌词还得是lrc格式的代码写的很罗嗦,急切希望帮改改CSS的代码​1.代码:<html >    <head> ...

  2. BZOJ_4591_[Shoi2015]超能粒子炮·改_Lucas定理

    BZOJ_4591_[Shoi2015]超能粒子炮·改_Lucas定理 Description 曾经发明了脑洞治疗仪&超能粒子炮的发明家SHTSC又公开了他的新发明:超能粒子炮·改--一种可以 ...

  3. 《Iterative-GAN》的算法伪代码整理

    花了一下午时间整理本人的论文Iterative-GAN的算法伪代码,由于篇幅较长,投会议方面的文章就不加入了,以后如果投期刊再说.留此存档.

  4. 前端性能优化之WebP

    此文已由作者吴维伟授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 前端性能优化是一件很琐碎的事情.它不像其它很多技术,在确切有限的步骤下就可以把功能做好.它就像是在打扫屋子, ...

  5. Python机器学习算法 — 朴素贝叶斯算法(Naive Bayes)

    朴素贝叶斯算法 -- 简介 朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法.最为广泛的两种分类模型是决策树模型(Decision Tree Model)和朴素贝叶斯模型(Naive Baye ...

  6. python 面向对象一 OOP

    一.面向对象和面相过程 面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数. 面 ...

  7. bzoj 1997: [Hnoi2010]Planar【瞎搞+黑白染色】

    脑补一下给出的图:一个环,然后有若干连接环点的边,我们就是要求这些边不重叠 考虑一下不重叠的情况,两个有交边一定要一个在环内一个在环外,所以把相交的边连边,然后跑黑白染色看是否能不矛盾即可(可能算个2 ...

  8. bzoj 1072: [SCOI2007]排列perm【状压dp】

    先写了个next_permutation结果T了,于是开始写状压 设f[s][i]为选取状态为s,选的数模d为i的方案数,去重的话直接除以每个数字的出现次数的阶乘即可 #include<iost ...

  9. bzoj 1178: [Apio2009]CONVENTION会议中心(少见做法掉落!)【贪心+二分】

    数组若干+手动二分一个的算法,bzoj rank8 ===============================废话分割线=================================== 我我 ...

  10. failed to push some refs to 'https://gitee.com/ftl_663/java-shop.git'

    1.git init 2.git add . 3.git commit  -m "init" 4.git remote add origin  https://gitee.com/ ...