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

  1. Which dispatch method would be used in Swift?-Existential Container

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

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

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

  3. 深入剖析Swift性能优化

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

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

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

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

  6. Unused Method(不再使用的方法)——Dead Code(死亡代码)

        系列文章目录:     使用Fortify进行代码静态分析(系列文章) Unused Method(不再使用的方法)    示例:  private bool checkLevel(strin ...

  7. [React] Use the useReducer Hook and Dispatch Actions to Update State (useReducer, useMemo, useEffect)

    As an alternate to useState, you could also use the useReducer hook that provides state and a dispat ...

  8. PHP 5.6 编译安装选项说明

    `configure' configures this package to adapt to many kinds of systems. Usage: ./configure [OPTION].. ...

  9. JSP实现word文档的上传,在线预览,下载

    前两天帮同学实现在线预览word文档中的内容,而且需要提供可以下载的链接!在网上找了好久,都没有什么可行的方法,只得用最笨的方法来实现了.希望得到各位大神的指教.下面我就具体谈谈自己的实现过程,总结一 ...

随机推荐

  1. 深度学习 dns tunnel检测 使用统计特征 全连接网络——精度99.8%

    代码如下: import numpy as np import tflearn from tflearn.layers.core import dropout from tflearn.layers. ...

  2. java对象序列化的理解

    1.java中的序列化时transient变量(这个关键字的作用就是告知JAVA我不可以被序列化)和静态变量不会被序列          化(下面是一个测试的例子) (实体带versionUUID,便 ...

  3. 洛谷P3004 宝箱Treasure Chest——DP

    题目:https://www.luogu.org/problemnew/show/P3004 似乎有点博弈的意思,但其实是DP: f[i][j] 表示 i~j 的最优结果,就可以进行转移: 注意两个循 ...

  4. String模块ascii_letters和digits

    Python3中String模块ascii_letters和digits方法,其中ascii_letters是生成所有字母,从a-z和A-Z,digits是生成所有数字0-9. 示例如下: Pytho ...

  5. 【黑金教程笔记之007】【建模篇】【Lab 06 SOS信号之二】—笔记

    控制模块的协调角色. 实验六用到了实验四的按键消抖模块debounce_module.v和实验五的sos_module.v. 设计思路: debounce_module.v看成一个输入,sos_mod ...

  6. ubuntu下7z文件的解压方法(转载)

    转自:http://qtlinux.blog.51cto.com/3052744/569406 打开终端,键入以下命令: apt-get install p7zip-full 控制台会打出以下信息: ...

  7. ThinkPHP3.2.3学习笔记1---控制器

    ThinkPHP是为了简化企业级应用开发和敏捷WEB应用开发而诞生的.最早诞生于2006年初,2007年元旦正式更名为ThinkPHP,并且遵循Apache2开源协议发布.ThinkPHP从诞生以来一 ...

  8. bzoj 3961: [WF2011]Chips Challenge【最小费用最大流】

    参考:https://blog.csdn.net/Quack_quack/article/details/50554032 神建图系列 首先把问题转为全填上,最少扣下来几个能符合条件 先考虑第2个条件 ...

  9. [Swift通天遁地]一、超级工具-(2)制作美观大方的环形进度条

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  10. 《windows核心编程系列》四谈谈进程的建立和终止

    第二部分:工作机理 第一章:进程 上一章介绍了内核对象,这一节开始就要不断接触各种内核对象了.首先要给大家介绍的是进程内核对象.进程大家都不陌生,它是资源和分配的基本单位,而进程内核对象就是与进程相关 ...