枚举内存使用

Sha*_*N A 4 ios swift

我想知道以下哪个占用更多的内存

struct Constants
{
    var age = 10
}
Run Code Online (Sandbox Code Playgroud)

或者

enum Constants
{
    case age = 10
}
Run Code Online (Sandbox Code Playgroud)

我也想知道之间的区别enum,并#define在存储器方面。有人可以帮我吗?

Lou*_*Lac 7

TLDR

Your struct would occupy the size of an Int, so on a recent Mac platform 64 bits (8 bytes). Your enumeration would normally occupy the size of an UInt8 so 8 bits (1 byte) but in this special case of a one case enum the size is 0 bit.


To understand that, here is some detail

An enumeration internally stores an integer value to match the cases against.

For instance this enum:

enum Direction {
    case east, west, south, north
}
Run Code Online (Sandbox Code Playgroud)

is roughly equivalent to:

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) }
}
Run Code Online (Sandbox Code Playgroud)

with some auto-generated methods to enable use in switch and if blocks, and some compiler magic to support switch statements exhaustivity.

Note that the rawValue is UInt8 so any Swift enumeration with less than 256 cases and without any associated value is 8 bits (1 byte).

If your enumeration has more than 255 cases the rawValue type upgrades to UInt16, and so on.

An enum containing an associated value is much more memory consuming, its size is the size of the rawValue + the size of the largest associated values.

For instance:

enum Shape {
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
}
Run Code Online (Sandbox Code Playgroud)

This enum would be 136bits (17bytes): 2 * size(Double) + size(UInt8) because the rectangle case has the largest associated values which are two Doubles.

In your case you defined an enum with custom raw values of type Int. Contrary to an enum with associated values this does not change the size of the enum at all. The Compiler just synthesises a computed property that return the custom rawValue.

For instance this enum with a String rawValue is still 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!"
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

Note 1

You can check the size of anything with:

MemoryLayout<TypeToSize>.size  // for a type
MemoryLayout.size(ofValue: yourValue)  /* for an instance */
Run Code Online (Sandbox Code Playgroud)

Be careful with reference types though, for instance a class is a reference type so it has the size of a pointer so 64 bits (8 bytes) on recent Mac platforms.


Note 2

If your enumeration has lots of heavy associated types you can turn it into a reference type with the indirect keyword so its size is one of a pointer but this cost you the dereferencing operation.


Note 3

In Swift the proper way to create a namespace is to define an enum with no case and only static properties.

The advantage of an enum over a struct is that such an enum cannot be instantiated which is the indented behaviour for a namespace.

In your case the proper way is:

MemoryLayout<TypeToSize>.size  // for a type
MemoryLayout.size(ofValue: yourValue)  /* for an instance */
Run Code Online (Sandbox Code Playgroud)

An empty enum or an enum with only one case has a size of 0 bit. However, the static property needs to be stored at some point, this cost you at least the size of the Int value.


Note 4

#define only exists in Objective-C AFAIK, not in Swift, so the canonic way to replicate this pattern is through the case-less enum (avoid global variables).


Mat*_*lak 3

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

#define sqare_2 sqrt(2.0)
Run Code Online (Sandbox Code Playgroud)

这将是一个坏主意,因为每次调用sqare_2它都会实际调用该sqrt函数并且不记得结果。好吧,除非进行编译时优化,但我希望您能明白这一点。因此 a#define与内存无关,因为它是编译时指令,而不是运行时指令。

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

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

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