枚举内存使用情况

时间:2016-03-10 09:22:09

标签: ios swift

我想知道以下哪一项占用更多内存

struct Constants
{
    var age = 10
}

enum Constants
{
    case age = 10
}

我还想知道enum#define在内存存储方面的区别。谁能帮助我?

2 个答案:

答案 0 :(得分:3)

#define或多或少是您在定义中输入的文本的精确副本。这意味着如果#define包含一个代码块,那么该块将在使用定义值的每个位置进行编译。

#define sqare_2 sqrt(2.0)

因为每次调用sqare_2它都会调用sqrt函数而不记得结果,这是个坏主意。好吧,除非进行编译时优化,但我希望你能得到图片。所以#define与内存无关,因为它是编译时指令,而不是运行时。

Swift中的枚举是纯粹的OOP,就像一个类。它会添加许多方法,例如rawValue,这会使元素非常大,性能不佳,并且可能有额外的内存消耗,但可能不是每个实例。枚举就好像它只有静态方法一样,因此创建实例不应该将内存膨胀到枚举原始值的类型之外。

结构占用的内存与您在其中定义的内存一样多,指针的内存在小结构中代表大部分数据。许多这些指针和实际大小可能与Swift的实现不同。例如,如果语言支持调配,则通常意味着每个类或结构将包含指向函数的指针,这些函数是其方法。如果是这样的话,我们希望每个编写的方法都会将至少32或64位添加到对象的大小,这也适用于从超类接收的方法。并且由于某些方法已经存在,例如比较,它将总是需要额外的内存。而且,由于添加了新版本,您可能希望尺寸不同。

对不起,我对你的具体案例没有答案,说哪个更大,可能你永远找不到答案。但一般来说,很难找到物体的大小。例如,您可能会得到String的大小始终只是指针的大小。如果你打开它虽然你可能会发现它有3个指针和3个整数的大小,但无论字符串的长度如何,大小都是常量。原因之一是指针中的一个指针保存了实际的字符串数据,而其余指针用于编码,长度等...你可以说明显的是字符串是实际文本数据的所有者,应该包括那些数据的大小,然后包装器的内容:UILabel的大小是否应该取决于它的文本长度?如果是这样,当2个标签共享相同的文本时会发生什么;然后,大小的总和将大于实际的内存消耗。

答案 1 :(得分:2)

TLDR

您的结构将占用一个Int的大小,因此在最近的Mac平台上为64位(8字节)。 您的枚举通常会占用一个Uint8的大小,因此为8位(1个字节),但是在这种特殊情况下,一个枚举的大小为0位。


要了解这一点,这里有一些细节

枚举在内部存储一个整数值以匹配大小写。

例如,这个枚举:

enum Direction {
    case east, west, south, north
}

大致等同于:

struct Direction {
    private var rawValue: UInt8
    
    private init(_ rawValue: UInt8) {
        self.rawValue = rawValue
    }

    static var east: Direction { Direction(0) }
    static var west: Direction { Direction(1) }
    static var south: Direction { Direction(2) }
    static var north: Direction { Direction(3) }
}

还有其他一些比较和打开方法。

请注意,rawValue是UInt8,因此任何少于256个大小写且没有任何关联值的Swift枚举都是8位(1字节)。

如果枚举超过255种,则rawValue类型将升级为UInt16,依此类推。

包含关联值的枚举会消耗更多的内存,其大小是rawValue的大小+最大关联值的大小。

例如:

enum Shape {
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
}

此枚举将为136位(17字节):2 * size(Double)+ size(UInt8),因为矩形大小写具有两个为Doubles的最大关联值。

在您的情况下,您定义了具有Int类型的自定义原始值的枚举。与带有关联值的枚举相反,这根本不会改变枚举的大小。编译器只是合成一个返回自定义rawValue的计算属性。

例如,带有字符串rawValue的该枚举仍然为1byte:

enum HelloWorld: String {
    case hello = "Hello, "
    case world = "world!"
}

// "Equivalent" to...
enum HelloWorld {
    case hello
    case world

    var rawValue: String {
        switch self {
        case .hello:
            return "Hello, "
        case .world:
            return "world!"
        }
    }
}


注释1

您可以使用以下方法检查任何东西的大小:

MemoryLayout<TypeToSize>.size  // for a type
MemoryLayout.size(ofValue: yourValue)  /* for an instance */

但是请注意引用类型,例如,类是引用类型,因此它具有指针的大小,因此在最近的Mac平台上为64位(8字节)。


注释2

如果您的枚举有很多繁重的关联类型,则可以使用indirect关键字将其转换为引用类型,以便其大小为恒定的指针大小,但会花费大量的取消引用操作。


注释3

在Swift中,创建名称空间的正确方法是定义一个没有大小写且只有静态属性的枚举。

枚举相对于结构的优势在于,无法实例化此枚举,这是名称空间的缩进行为。

您的情况下正确的方法是:

enum Constants {
    static var age = 10
}

// Example of use...
let birthYear = 2020 - Constants.age

此类枚举无法实例化,因此您无法真正谈论其大小。但是,age属性存储在内存中(只有一次无法实例化枚举),其大小为Int


注释4

#define仅存在于Objective-C AFAIK中,而不存在于Swift中,因此典型的复制这种模式的方法是通过不区分大小写的枚举(避免使用全局变量)。