【发布时间】:2020-09-15 15:26:57
【问题描述】:
我阅读了有关 Swift 中枚举大小的文档,这是我的理解:
这个简单的只有一个“标签”来区分大小写,默认情况下是一个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 字节而不是T 时的9 字节是引用类型。这种优化是在this SO question about Rust 中提出的,我相信它与 Swift 关于枚举的行为相同。
例如对于这个简单的引用类型,MemoryLayout 返回一个 8 字节的大小:
class Person {
var name: String
init(name: String) {
self.name = name
}
}
let p = Opt.some(Person(name: "Bob")) // 8 bytes
问题
我想不通的是这个枚举的大小(仍然是当 T 是引用类型时):
enum Opt<T> {
case none
case secondNone
case some(T)
}
为什么这个也是8字节,根据MemoryLayout?
据我了解,它应该是 9 个字节。 NULL 优化是唯一可能的,因为none 可以用 NULL 表示,但在我的示例中,secondNone 没有“第二个”NULL 值,所以这里应该需要一个标签来区分情况。
编译器是否会因此自动将此枚举转换为引用类型(类似于indirect 枚举)?这将解释 8 字节的大小。如何验证最后一个假设?
【问题讨论】:
-
你在哪里测试这段代码?在真实项目中的发布版本上?如果你在操场上做,那是不可靠的,因为操场没有使用优化。
-
是的,那是一个游乐场,我将在一个常规项目中对其进行全面优化。
-
使用
-O优化得到了同样的结果。
标签: swift enums size memory-layout