微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Existential Container 与符合协议的结构体实例的关系

如何解决Existential Container 与符合协议的结构体实例的关系

我正在尝试了解如何找到协议方法的实现。

我知道 Swift 使用 Existential Container 在堆栈内存中进行固定大小的存储,它管理如何描述内存中 struct 的实例。它有一个价值见证表(VWT)和协议见证表(PWT)

VWT 知道如何在 struct 实例(它们的生命周期)中管理实际值,而 PWT 知道协议方法的实现。

但我想知道结构实例和“存在容器”之间的关系。

struct 的实例是否有指向存在容器的指针?

struct 的实例如何知道其存在容器?

解决方法

我的理解:

Struct 不知道存在容器/值见证表/协议见证表在哪里,编译器知道。如果某处需要,编译器将它们传递到那里。

,

前言:我不知道你有多少背景知识,所以我可能会过度解释以确保我的答案清楚。

此外,我正在尽我所能,靠记忆做到这一点。我可能会混淆一些细节,但希望这个答案至少可以引导您进一步阅读。

另见:


在 Swift 中,协议可以用作“类型”或通用约束。后一种情况看起来像这样:

protocol SomeProtocol {}
struct SomeConformerSmall: SomeProtocol {
    // No ivars
}
struct SomeConformerBig: SomeProtocol {
    let a,b,c,d,e,f,g: Int // Lots of ivars
}

func fooUsingGenerics<T: SomeProtocol>(_: T) {}

let smallObject = SomeConformerSmall()
let bigObject = SomeConformerBig()

fooUsingGenerics(smallObject)
fooUsingGenerics(bigObject)

该协议在编译时用作类型检查的约束,但在运行时(大多数情况下)没有什么特别的事情发生。大多数情况下,编译器会生成 foo 函数的单态变体,就好像您已经定义了 fooUsingGenerics(_: SomeConformerSmall)fooUsingGenerics(_: SomeConformerBig) 一样。

当协议“像类型一样使用”时,它看起来像这样:

func fooUsingProtcolExistential(_: SomeProtocol) {}

fooUsingGenerics(smallObject)
fooUsingGenerics(bigObject)

如您所见,可以使用 smallObjectbigObject 调用此函数。问题是这两个对象的大小不同。这是一个问题:如果参数可以是不同的大小,编译器如何知道需要为该函数的参数分配多少堆栈空间?它必须做些什么来帮助fooUsingProtcolExistential适应这一点。

现有容器是解决方案。当你传递一个需要协议类型的值时,Swift 编译器会生成代码,为你自动将该值装箱到“存在容器”中。按照目前的定义,一个存在容器的大小为 4 个字:

  • 第一个词是指向协议见证表的指针(稍后会详细介绍)
  • 接下来的三个词是值的内联存储。

当存储的值小于 3 个字时(例如 SomeConformerSmall),该值直接内联打包到 3 个字的缓冲区中。如果值的大小超过 3 个字(例如 SomeConformerSmall),则在堆上分配一个 ARC 管理的框,并将值复制到那里。然后将指向此框的指针复制到存在容器的第一个字中(最后两个字未使用,IIRC)。

这引入了一个新问题:假设 fooUsingProtcolExistential 想要将其参数转发到另一个函数。它应该如何通过EC? fooUsingProtcolExistential 不知道 EC 是否包含值内联(在这种情况下,传递 EC 只需要复制其 4 个内存字),或堆分配(在这种情况下,传递 EC 还需要一个ARC 保留在该堆分配的缓冲区上)。

为了解决这个问题,协议见证表包含一个指向值见证表 (VWT) 的指针。每个 VWT 定义了一组标准的函数指针,定义了如何分配、复制、删除 EC 等。每当需要以某种方式操作存在的协议时,VWT 都准确地定义了如何操作。

所以现在我们有一个固定大小的容器(它解决了我们异构大小的参数传递问题),以及一种移动容器的方法。我们实际上可以用它做什么?

至少,此协议类型的值必须至少定义协议定义的必需成员(初始化程序、属性(存储或计算)、函数和下标)。

但是每个符合标准的类型可能以不同的方式实现这些成员。例如。某些结构可能通过直接定义方法来满足方法要求,但另一个类可能通过从超类继承方法来满足它。有些可能将属性实现为存储属性,其他可能实现为计算属性等。

处理这些不兼容性是协议见证表的主要目的。每个协议一致性有一个这些表(例如一个用于 SomeConformerSmall,一个用于 SomeConformerBig)。它们包含一组函数指针,指向协议要求的实现。虽然指向的函数可能在不同的地方,但 PWT 的布局是一致的,因为协议是符合的。因此,fooUsingProtcolExistential 能够查看 EC 的 PWT,并使用它来查找协议方法的实现并调用它。

简而言之:

  • 一个 EC 包含一个 PWT 和一个值(内联或间接)
  • PWT 指向 VWT

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。