Lou*_*Lac 8 size enums memory-layout swift
我阅读了有关 Swift 中枚举大小的文档,这是我的理解:
这个简单的仅包含一个“标签”来区分情况,默认情况下是一个UInt8
值,即small = 0
,medium = 1
等等。所以Size
的大小为 1 个字节,可以用 来验证MemoryLayout<Size>.size
。我还注意到,如果枚举的情况超过 255 个,显然标记大小会升级为 2 个字节。
enum Size {
case small
case medium
case large
}
Run Code Online (Sandbox Code Playgroud)
第二种情况,如果枚举具有关联值,则其行为类似于联合。在这种情况下,枚举大小是标记的大小加上最大关联值的大小。在以下示例中,大小为 1 字节 + 16 字节(字符串),即 17 字节,也可以使用 进行验证MemoryLayout
。
enum Size {
case small
case medium
case large
}
Run Code Online (Sandbox Code Playgroud)
最后一种情况,由于 Swift 是一种安全语言,使用标准非不安全 Swift 代码的引用始终有效,即始终指向内存中的值。T
这允许编译器在是引用类型时优化此类枚举:
enum Opt<T> {
case none
case some(T)
}
Run Code Online (Sandbox Code Playgroud)
这里类型的实例T
不能为nil
(NULL),因此编译器在这种none
情况下使用这个特殊值,因此当它是引用类型Opt
时,其大小为 8 字节而不是 9 字节。T
这种优化是在关于 Rust 的 SO 问题中提出的,我相信它在枚举方面与 Swift 具有相同的行为。
例如,对于这个简单的引用类型,MemoryLayout
返回 8 个字节的大小:
class Person {
var name: String
init(name: String) {
self.name = name
}
}
let p = Opt.some(Person(name: "Bob")) // 8 bytes
Run Code Online (Sandbox Code Playgroud)
我无法弄清楚这个枚举的大小(仍然是当 T 是引用类型时):
enum Opt<T> {
case none
case secondNone
case some(T)
}
Run Code Online (Sandbox Code Playgroud)
根据 ,为什么这个也是 8 字节MemoryLayout
?
按照我的理解,它应该是9个字节。NULL 优化是唯一可能的,因为none
可以用 NULL 表示,但在我的示例中没有“第二个”NULL 值secondNone
,因此这里应该需要一个标签来区分情况。
编译器是否会因此自动将此枚举转换为引用类型(类似于枚举indirect
)?这可以解释 8 字节的大小。我如何验证最后一个假设?
如果数据类型的二进制表示具有额外的居民,即具有该类型的大小和对齐方式的位模式,但不形成该类型的有效值,则它们用于表示无数据情况,并且额外的居民按顺序排列与声明顺序中的无数据案例匹配的升序数值。
您的示例有更多案例:
enum Opt<T> {
case a, b, c, d, e, f, g, h, i, j, k
case l, m, n, o, p, q, r, s, t, u, v
case some(T)
}
class Person {
var name: String
init(name: String) { self.name = name }
}
print(unsafeBitCast(Opt<Person>.a, to: UnsafeRawPointer.self))
// 0x0000000000000000
print(unsafeBitCast(Opt<Person>.b, to: UnsafeRawPointer.self))
// 0x0000000000000002
print(unsafeBitCast(Opt<Person>.v, to: UnsafeRawPointer.self))
// 0x000000000000002a
let p = Person(name: "Bob")
print(unsafeBitCast(Opt.some(p), to: UnsafeRawPointer.self))
// 0x00006030000435d0
Run Code Online (Sandbox Code Playgroud)
显然,0x0
、0x2
、 ...0x2a
是指针的无效位模式,因此用于其他情况。
精确的算法似乎没有文档记录,可能需要检查 Swift 编译器源代码。