关联值是引用类型时的快速枚举大小

问题描述 投票:3回答:1

我阅读了有关Swift中枚举大小的文档,这是我的理解:

这个简单的标签只带有一个'tag'来区分大小写,默认情况下是UInt8值,即small = 0medium = 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个字节的大小。我如何验证这个最后的假设?

swift enums size memory-layout
1个回答
1
投票

来自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编译器的源代码。

© www.soinside.com 2019 - 2024. All rights reserved.