What is Swift enum byte representation?

2019-07-02 03:27发布

问题:

I have the following enum:

enum Enum: UInt8 
{
  case A = 0x00
  case B = 0x01
  case C = 0x10
}

I use the following code to convert it to NSData:

var value = Enum.C
let data = NSData(bytes: &value, length: 1)

Naturally, I expect data to contain 0x10, however, it contains 0x02.

For Enum.A it contains 0x00 and for Enum.B it contains 0x01.

To me, it looks like it stores an index of the value instead of its actual raw data. Could someone explain this behavior?

P.S. If I use rawValue it works perfectly. However, I want to understand the reason behind, since because of it I cannot create a generic function to convert values to NSData.

回答1:

The Swift ABI is still work in progress (expected to be fixed with Swift 4). The way enums are represented in memory is described here.

Your case is a "c-like enum" because is has...

  • two or more cases
  • no associated values

Quoting the ABI document:

the enum is laid out as an integer tag with the minimal number of bits to contain all of the cases. [...] The cases are assigned tag values in declaration order.

The crucial information here is the "minimal number of bits". This means that (for your case) instances should fit into two bits (as you have three cases). The rawValue 0x10 would need five bits—which would be in conflict with the ABI.

The compiler probably uses static tables to convert between instances of Enum and their rawValue (and back).

Here's an example that highlights this characteristic of the ABI:

enum Enum: UInt32
{
    case A = 0
    case B = 0xffffffff    // value that does not fit into one byte
}

assert(sizeof(Enum.self) == 1)


回答2:

Each enum case is given a standard order value. Which is what you are getting when you don't use .rawValue.

For example, if you change your enum to:

enum Enum: UInt8 
{
  case A = 0x00
  case B = 0x01
  case B2 = 0x0A
  case C = 0x10
}

Then, when executing

var value = Enum.C
let data = NSData(bytes: &value, length: 1)

data will be 3, because A = 0, B = 1, B2 = 2, C = 3