一、测试代码:

//protocol DiceGameDelegate: AnyObject {

//}

//

//@objc protocol OcProtocol{

//    @objc func OcFunc()

//}

protocol SeedProtocol {

func NormalFunc()

func ExtenImpFunc()

//@objc func OcFunc()

}

extension SeedProtocol{

func ExtenImpFunc(){}

func ExtenDefineFunc(){}

}

struct NormalStruct:SeedProtocol {

func NormalFunc(){}

}

struct FullImpStruct:SeedProtocol {

func NormalFunc(){}

func ExtenImpFunc(){}

func ExtenDefineFunc(){}

}

struct EmptyStruct {}

extension EmptyStruct:SeedProtocol{

func NormalFunc(){}

}

func CallProtocol(_ object:SeedProtocol)

{

object.NormalFunc()

object.ExtenImpFunc()

object.ExtenDefineFunc()

}

func DoTest(){

CallProtocol(NormalStruct())

//    CallProtocol(FullImpStruct())

//    CallProtocol(EmptyStruct())

}

二、见证容器

  • Existential Container

这是一个最普通的 Existential Container。

  • 前三个word:Value buffer。用来存储Inline的值,如果word数大于3,则采用指针的方式,在堆上分配对应需要大小的内存
  • 第四个word:Value Witness Table(VWT)。每个类型都对应这样一个表,用来存储值的创建,释放,拷贝等操作函数。(管理 Existential Container 生命周期)
  • 第五个word:Protocol Witness Table(PWT),用来存储协议的函数。

用伪代码表示如下:

所以,对于上文代码中的 Point 和 Line 最后的数据结构大致如下:

这里需要注意的几个点:

  • 在 ABI 稳定之前 value buffer 的 size 可能会变,对于是不是 3个 word 还在 Swift 团队还在权衡.
  • Existential Container 的 size 不是只有 5 个 word。示例如下:

对于这个大小差异最主要在于这个 PWT 指针,对于 Any 来说,没有具体的函数实现,所以不需要 PWT 这个指针,但是对于 ProtocolOne&ProtocolTwo 的组合协议,是需要两个 PWT 指针来表示的。

OK,由于 Existential Container 的引入,我们可以将协议作为类型来解决 平凡类型 没有继承的问题,所以 Struct:Protocol 和 抽象类就越来越像了。

https://www.cnblogs.com/feng9exe/p/9680824.html

三、虚函数表:

sil_witness_table hidden NormalStruct: ProtocolCase module ProtocolCase {

method #ProtocolCase.NormalFunc!1: <Self where Self : ProtocolCase> (Self) -> () -> () : @protocol witness for ProtocolCase.ProtocolCase.NormalFunc() -> () in conformance ProtocolCase.NormalStruct : ProtocolCase.ProtocolCase in ProtocolCase // protocol witness for ProtocolCase.NormalFunc() in conformance NormalStruct

method #ProtocolCase.ExtenImpFunc!1: <Self where Self : ProtocolCase> (Self) -> () -> () : @protocol witness for ProtocolCase.ProtocolCase.ExtenImpFunc() -> () in conformance ProtocolCase.NormalStruct : ProtocolCase.ProtocolCase in ProtocolCase // protocol witness for ProtocolCase.ExtenImpFunc() in conformance NormalStruct

}

sil_witness_table hidden FullImpStruct: ProtocolCase module ProtocolCase {

method #ProtocolCase.NormalFunc!1: <Self where Self : ProtocolCase> (Self) -> () -> () : @protocol witness for ProtocolCase.ProtocolCase.NormalFunc() -> () in conformance ProtocolCase.FullImpStruct : ProtocolCase.ProtocolCase in ProtocolCase // protocol witness for ProtocolCase.NormalFunc() in conformance FullImpStruct

method #ProtocolCase.ExtenImpFunc!1: <Self where Self : ProtocolCase> (Self) -> () -> () : @protocol witness for ProtocolCase.ProtocolCase.ExtenImpFunc() -> () in conformance ProtocolCase.FullImpStruct : ProtocolCase.ProtocolCase in ProtocolCase // protocol witness for ProtocolCase.ExtenImpFunc() in conformance FullImpStruct

}

sil_witness_table hidden EmptyStruct: ProtocolCase module ProtocolCase {

method #ProtocolCase.NormalFunc!1: <Self where Self : ProtocolCase> (Self) -> () -> () : @protocol witness for ProtocolCase.ProtocolCase.NormalFunc() -> () in conformance ProtocolCase.EmptyStruct : ProtocolCase.ProtocolCase in ProtocolCase // protocol witness for ProtocolCase.NormalFunc() in conformance EmptyStruct

method #ProtocolCase.ExtenImpFunc!1: <Self where Self : ProtocolCase> (Self) -> () -> () : @protocol witness for ProtocolCase.ProtocolCase.ExtenImpFunc() -> () in conformance ProtocolCase.EmptyStruct : ProtocolCase.ProtocolCase in ProtocolCase // protocol witness for ProtocolCase.ExtenImpFunc() in conformance EmptyStruct

}

四、动态派发机制

// CallProtocol(_:)

sil hidden @ProtocolCase.CallProtocol(ProtocolCase.SeedProtocol) -> () : $@convention(thin) (@in_guaranteed SeedProtocol) -> () {

// %0                                             // users: %8, %5, %2, %1

bb0(%0 : $*SeedProtocol):

debug_value_addr %0 : $*SeedProtocol, let, name "object", argno 1 // id: %1

%2 = open_existential_addr immutable_access %0 : $*SeedProtocol to $*@opened("C73DB366-4AFB-11E9-B4BE-ACBC329BAA1D") SeedProtocol // users: %4, %4, %3

%3 = witness_method $@opened("C73DB366-4AFB-11E9-B4BE-ACBC329BAA1D") SeedProtocol, #SeedProtocol.NormalFunc!1 : <Self where Self : SeedProtocol> (Self) -> () -> (), %2 : $*@opened("C73DB366-4AFB-11E9-B4BE-ACBC329BAA1D") SeedProtocol : $@convention(witness_method: SeedProtocol) <τ_0_0 where τ_0_0 : SeedProtocol> (@in_guaranteed τ_0_0) -> () // type-defs: %2; user: %4

%4 = apply %3<@opened("C73DB366-4AFB-11E9-B4BE-ACBC329BAA1D") SeedProtocol>(%2) : $@convention(witness_method: SeedProtocol) <τ_0_0 where τ_0_0 : SeedProtocol> (@in_guaranteed τ_0_0) -> () // type-defs: %2

%5 = open_existential_addr immutable_access %0 : $*SeedProtocol to $*@opened("C73E23AA-4AFB-11E9-B4BE-ACBC329BAA1D") SeedProtocol // users: %7, %7, %6

%6 = witness_method $@opened("C73E23AA-4AFB-11E9-B4BE-ACBC329BAA1D") SeedProtocol, #SeedProtocol.ExtenImpFunc!1 : <Self where Self : SeedProtocol> (Self) -> () -> (), %5 : $*@opened("C73E23AA-4AFB-11E9-B4BE-ACBC329BAA1D") SeedProtocol : $@convention(witness_method: SeedProtocol) <τ_0_0 where τ_0_0 : SeedProtocol> (@in_guaranteed τ_0_0) -> () // type-defs: %5; user: %7

%7 = apply %6<@opened("C73E23AA-4AFB-11E9-B4BE-ACBC329BAA1D") SeedProtocol>(%5) : $@convention(witness_method: SeedProtocol) <τ_0_0 where τ_0_0 : SeedProtocol> (@in_guaranteed τ_0_0) -> () // type-defs: %5

%8 = open_existential_addr immutable_access %0 : $*SeedProtocol to $*@opened("C73E2738-4AFB-11E9-B4BE-ACBC329BAA1D") SeedProtocol // users: %10, %10

// function_ref SeedProtocol.ExtenDefineFunc()

%9 = function_ref @(extension in ProtocolCase):ProtocolCase.SeedProtocol.ExtenDefineFunc() -> () : $@convention(method) <τ_0_0 where τ_0_0 : SeedProtocol> (@in_guaranteed τ_0_0) -> () // user: %10

%10 = apply %9<@opened("C73E2738-4AFB-11E9-B4BE-ACBC329BAA1D") SeedProtocol>(%8) : $@convention(method) <τ_0_0 where τ_0_0 : SeedProtocol> (@in_guaranteed τ_0_0) -> () // type-defs: %8

%11 = tuple ()                                  // user: %12

return %11 : $()                                // id: %12

} // end sil function 'ProtocolCase.CallProtocol(ProtocolCase.SeedProtocol) -> ()'

// DoTest()

sil hidden @ProtocolCase.DoTest() -> () : $@convention(thin) () -> () {

bb0:

%0 = alloc_stack $SeedProtocol                  // users: %9, %8, %7, %4

%1 = metatype $@thin NormalStruct.Type          // user: %3

// function_ref NormalStruct.init()

%2 = function_ref @ProtocolCase.NormalStruct.init() -> ProtocolCase.NormalStruct : $@convention(method) (@thin NormalStruct.Type) -> NormalStruct // user: %3

%3 = apply %2(%1) : $@convention(method) (@thin NormalStruct.Type) -> NormalStruct // user: %5

%4 = init_existential_addr %0 : $*SeedProtocol, $NormalStruct // user: %5

store %3 to %4 : $*NormalStruct                 // id: %5

// function_ref CallProtocol(_:)

%6 = function_ref @ProtocolCase.CallProtocol(ProtocolCase.SeedProtocol) -> () : $@convention(thin) (@in_guaranteed SeedProtocol) -> () // user: %7

%7 = apply %6(%0) : $@convention(thin) (@in_guaranteed SeedProtocol) -> ()

destroy_addr %0 : $*SeedProtocol                // id: %8

dealloc_stack %0 : $*SeedProtocol               // id: %9

%10 = tuple ()                                  // user: %11

return %10 : $()                                // id: %11

} // end sil function 'ProtocolCase.DoTest() -> ()'

五、补充说明

Existential Container Layout

Values of protocol type, protocol composition type, or Any type are laid out using existential containers (so-called because these types are "existential types" in type theory).

Opaque Existential Containers

If there is no class constraint on a protocol or protocol composition type, the existential container has to accommodate a value of arbitrary size and alignment. It does this using a fixed-size buffer, which is three pointers in size and pointer-aligned. This either directly contains the value, if its size and alignment are both less than or equal to the fixed-size buffer's, or contains a pointer to a side allocation owned by the existential container. The type of the contained value is identified by its type metadata record, and witness tables for all of the required protocol conformances are included. The layout is as if declared in the following C struct:

struct OpaqueExistentialContainer {

void *fixedSizeBuffer[3];

Metadata *type;

WitnessTable *witnessTables[NUM_WITNESS_TABLES];

};

Class Existential Containers

If one or more of the protocols in a protocol or protocol composition type have a class constraint, then only class values can be stored in the existential container, and a more efficient representation is used. Class instances are always a single pointer in size, so a fixed-size buffer and potential side allocation is not needed, and class instances always have a reference to their own type metadata, so the separate metadata record is not needed. The layout is thus as if declared in the following C struct:

struct ClassExistentialContainer {

HeapObject *value;

WitnessTable *witnessTables[NUM_WITNESS_TABLES];

};

Note that if no witness tables are needed, such as for the "any class" type protocol<class> or an Objective-C protocol type, then the only element of the layout is the heap object pointer. This is ABI-compatible with id and id <Protocol> types in Objective-C.

https://github.com/apple/swift/blob/master/docs/ABI/TypeLayout.rst

swift protocol 见证容器 虚函数表 与 动态派发的更多相关文章

  1. C++ 中的虚函数表及虚函数执行原理

    为了实现虚函数,C++ 使用了虚函数表来达到延迟绑定的目的.虚函数表在动态/延迟绑定行为中用于查询调用的函数. 尽管要描述清楚虚函数表的机制会多费点口舌,但其实其本身还是比较简单的. 首先,每个包含虚 ...

  2. swift class的虚函数表、扩展、@objc修饰、虚函数的派发方式研究

    swift class的虚函数表.扩展.@objc修饰的研究 工具: swiftc -emit-sil BaseClass.swift | xcrun swift-demangle > Clas ...

  3. C++ 虚函数表解析

    转载:陈皓 http://blog.csdn.net/haoel 前言 C++中 的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实 ...

  4. C++ 多态、虚函数机制以及虚函数表

    1.非virtual函数,调用规则取决于对象的显式类型.例如 A* a  = new B(); a->display(); 调用的就是A类中定义的display().和对象本体是B无关系. 2. ...

  5. C++迟后联编和虚函数表

    先看一个题目: class Base { public: virtual void Show(int x) { cout << "In Base class, int x = & ...

  6. C++ 知道虚函数表的存在

    今天翻看陈皓大大的博客,直接找关于C++的东东,看到了虚函数表的内容,找一些能看得懂的地方记下笔记. 0 引子 类中存在虚函数,就会存在虚函数表,在vs2015的实现中,它存在于类的头部. 假设有如下 ...

  7. C++虚函数和虚函数表

    前导 在上面的博文中描述了基类中存在虚函数时,基类和派生类中虚函数表的结构. 在派生类也定义了虚函数时,函数表又是怎样的结构呢? 先看下面的示例代码: #include <iostream> ...

  8. C++ Daily 《5》----虚函数表的共享问题

    问题: 包含一个以上虚函数的 class B, 它所定义的 对象是否共用一个虚函数表? 分析: 由于含有虚函数,因此对象内存包含了一个指向虚函数表的指针,但是这个指针指向的是同一个虚函数表吗? 实验如 ...

  9. C++虚函数表

    大家知道虚函数是通过一张虚函数表来实现的.在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承.覆盖的问题,其内容真是反应实际的函数.这样,在有虚函数的类的实例中,这个表分配在了这个实例的内存中 ...

随机推荐

  1. 使用flexible适配移动端h5页面

    flexible是淘宝提供的一套REM手机适配的库,用法也非常简单 首先,在页面中引入相关资源 包括flexible.js和flexible_css.js(用于清除默认样式),或者通过cdn方式引入 ...

  2. [Mysql]——通过例子理解事务的4种隔离级别

    SQL标准定义了4种隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的. 低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销. 首先,我们使用 test 数据库, ...

  3. Hyperledger Fabric密码模块系列之BCCSP(五) - 国密算法实现

    Talk is cheap, show me your code. 代码也看了,蛋也扯了,之后总该做点什么.响应国家政策,把我们的国密算法融合进去吧--  先附两张bccsp下国密算法的设计实现图. ...

  4. 【手记】.net正则行尾匹配符$的问题

    本来想用正则Split一下sql语句中简单场景的的GO,于是用^GO$(配合忽略大小写和多行模式),可居然连这种情况都搞不掂: go 如果删掉$就能匹配了,但这显然不是办法,遂又在VS的C#交互窗口. ...

  5. fllume 入门

    flume flume 简介及核心概念 什么是flume Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集.聚合和传输的系统,目前是Apache的顶级项目.Flume支持 ...

  6. 给OkHttp Client添加socks代理

    Okhttp的使用没有httpClient广泛,网上关于Okhttp设置代理的方法很少,这篇文章完整介绍了需要注意的方方面面. 上一篇博客中介绍了socks代理的入口是创建java.net.Socke ...

  7. 消息队列&Celery&RabbitMQ&zeromq

    一.消息队列 什么是消息队列? “消息队列”是在消息的传输过程中保存消息的容器. “消息”是在两台计算机间传送的数据单位.消息可以非常简单,例如只包含文本字符串:也可以更复杂,可能包含嵌入对象. 消息 ...

  8. git命令详解( 二 )

    这是git详解的第二篇,最近这个加班有点虚,所以文章也写的比较懒了,一到加班不得已,保温杯里泡枸杞啊,不扯了,今天我们来看看git的第二篇 这篇内容主要是git的一些远程命令 远程仓库 git clo ...

  9. 【代码笔记】Web-HTML-基础

    一,效果图. 二,代码. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...

  10. 【读书笔记】iOS-WiFi长连接

    如果你的应用需要一个持久的WiFi长连接,你可以通过设置应用的Info.plist文件中的UIRequiresPersistentWiFi配置项的Boolean值来达到目的.如果这个配置项的值为YES ...