我阅读了有关Swift中枚举大小的文档,这是我的理解:
这个简单的标签只带有一个'tag'来区分大小写,默认情况下是UInt8
值,即small = 0
,medium = 1
等。因此,Size
的大小为1个字节,可以用MemoryLayout<Size>.size
进行验证。我还注意到,如果枚举有255个以上的案例,则显然标签大小将升级为2个字节。
enum Size {
case small
case medium
case large
}
第二种情况,如果枚举具有关联值,则其行为类似于并集。在这种情况下,枚举大小是标签的大小加上最大关联值的大小。在下面的示例中,大小为1字节+ 16字节(字符串),因此为17字节,也可以使用MemoryLayout
进行验证。
enum Value {
case int(Int)
case double(Double)
case string(String)
case bool(Bool)
}
最后一种情况,因为Swift是一种安全的语言,引用始终使用标准的非安全Swift代码有效,即始终指向内存中的值。当T
是引用类型时,这使编译器可以优化此类枚举:
enum Opt<T> {
case none
case some(T)
}
这里类型为T
的实例不能以nil
表示(NULL),因此编译器在none
情况下使用此特殊值,因此Opt
的大小为8个字节,而不是9个字节。参考类型。在T
关于Rust的问题中提出了这种优化,我相信它的行为与Swift有关的枚举相同。
例如,对于这种简单的引用类型,this返回的大小为8个字节:
MemoryLayout
我不知道这个枚举的大小(当T是引用类型时仍然如此:
class Person {
var name: String
init(name: String) {
self.name = name
}
}
let p = Opt.some(Person(name: "Bob")) // 8 bytes
根据enum Opt<T> {
case none
case secondNone
case some(T)
}
,为什么这也是8个字节?
据我所知,应该为9个字节。 NULL优化是唯一可行的,因为MemoryLayout
可以用NULL表示,但是在我的示例中none
没有'second'NULL值,因此此处应使用标记来区分情况。
编译器是否因此而自动将该枚举转换为引用类型(类似于secondNone
枚举)?这将解释8个字节的大小。我如何验证这个最后的假设?
来自indirect
:
如果数据类型的二进制表示形式具有额外的居住者,即具有该类型的大小和对齐方式但不形成该类型的有效值的位模式,则它们用于表示无数据的情况,并带有额外的居住者以升序顺序匹配无数据大小写的数字顺序。
您的更多情况示例:
Type Layout: Single-Payload Enums
显然,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
,0x0
,...,0x2
是指针的无效位模式,因此用于其他情况。
精确的算法似乎没有记载,可能需要检查Swift编译器的源代码。