In the previous article, we looked at the memory layout of enumerations of common types. This article looks at the memory layout of enumerations of common types, starting with a small example

enum A {
    enum B {
        case b1
        case b2
        case b3
    }
    case a1(a:B)
    case a2(a:B)
    case a3(a:B)
}
var a = A.a2(a: .b2, b: .b2)
print(MemoryLayout<A>.size)
print(MemoryLayout<A>.stride)
Copy the code

According to our analysis in the previous article, enumeration A should have A memory size of 1+1+1=3 bytes, but it turns out to be 2 bytes, so let’s look at the memory layout of the variable A

Let’s analyze it in binary

If the associated value is an enumeration type, Apple optimizes it further. Instead of using bytes to determine whether a case can be stored, it uses bits.

A byte can store 256 values. The 3 cases of enumeration A occupy 2 binary bits, and the remaining 6 binary bits can store at most 64 values. Let’s verify that there are 64 cases in B

enum A { enum B { case b1 case b2 case b3 case b4 ... Case case b64} case a1(a:B) case A2 (a:B) case A3 (a:B) .b64) print(MemoryLayout<A>.size) print(MemoryLayout<A>.stride) print("end")Copy the code

As expected, take a look at a’s memory layout

If the enumeration B contains 65 cases, it should be +1, that is, 2 bytes, and 0x41 and 0x01 respectively

enum A { enum B { case b1 case b2 case b3 case b4 ... Case case b64 case B65} case a1(a:B) case A2 (a:B) case A3 (a:B)} var a = a.a2 (a:B) .b65) print(MemoryLayout<A>.size) print(MemoryLayout<A>.stride) print("end")Copy the code

Bytes +1, as expected, take a look at a’s memory layout

This step is not as expected, that is, Apple does not store the case in A in bytes, and analyze it in binary

Continue to verify

enum A { enum B { case b1 case b2 case b3 case b4 ... Case case b64 case B65} case a1(a:B) case A2 (a:B) case A3 (a:B)} var a = a.3 (a:B) .b65) print(MemoryLayout<A>.size) print(MemoryLayout<A>.stride) print("end")Copy the code

If a = a.a3, the memory layout of a should be

verify

As expected!!

If there is only one argument in A case, what if there is multiple arguments in B?

enum A { enum B { case b1 case b2 case b3 case b4 ... Case case b64 case B65} Case a1(a:B, B :B) case A2 (a:B, B :B) case A3 (a:B, B :B)} a = a.a2 (a:B, B :B) .b65) print(MemoryLayout<A>.size) print(MemoryLayout<A>.stride) print("end")Copy the code

In this case, the parameter A of a.a3 takes up 7 binary bits, and the parameter B also takes up 7 binary bits, so the two bytes can each provide 1 binary bit for a to store the case

What if the parameters are not of the same type?

enum A { enum B { case b1 ... Pretend there are 65 cases case b65} enum C {case c1 case C2 case C3 case C4 Case C5 Case C6 case C7 case C8} Case A1 (a:B, B :C) case a2(a:B,b:C) case a3(a:B,b:C) case a4(a:B,b:C) case a5(a:B,b:C) case a6(a:B,b:C) case a7(a:B,b:C) case a8(a:B,b:C) } var a = A.a8(a: .b65,b: .c7) print(MemoryLayout<A>.size) print(MemoryLayout<A>.stride) print("end")Copy the code

This is a special case where the second argument is of type enum C and takes up three binary bits

enum A { enum B { case b1 ... Pretend there are 65 cases case b65} enum C {case c1 case C2 case C3 case C4 Case C5 Case C6 case C7 case C8} Case A1 (a:B, B :C) case Case A3 (a:B, B :C) Case A4 (a:B, B :C) Case A5 (a:B, B :C) Case A6 (a:B, B :C) case A7 (a:B, B :C) case A3 (a:B, B :C) case A4 (a:B, B :C a8(a:B,b:C) } var a = A.a8(a: .b65,b: .c7) print(MemoryLayout<A>.size) print(MemoryLayout<A>.stride) print("end")Copy the code

enum A { enum B { case b1 ... Pretend there are 65 cases case b65} enum C {case c1 case C2 case C3 case C4 Case C5 Case C6 case C7 case C8} Case A1 (a:B, B :C) case a2(a:B,b:Int8) case a3(a:B,b:C) case a4(a:B,b:C) case a5(a:B,b:C) case a6(a:B,b:C) case a7(a:B,b:C) case a8(a:B,b:C) } var a = A.a8(a: .b65,b: .c7) print(MemoryLayout<A>.size) print(MemoryLayout<A>.stride) print("end")Copy the code

Conclusion:

Rule for calculating case size in enumerations: The sum of the bits used by the first n-1 argument must be the smallest positive integer multiple of the bits or 64 bits used by the NTH argument

enum A{
    
    enum B {
        case b1
        case b2
        case b3
        case b4
    }
     case a1(t1:Int)
     case a2(t1:Int8,t2:B,t3:Int)
     case a3(t1:Int8,t2:Int,t3:B,t4:Int32)
     case a4(t1:Int8,t2:Int,t3:Int16,t4:Int,t5:B,t6:String)
}

print(MemoryLayout<A>.size)
print(MemoryLayout<A>.stride)
Copy the code

A1 is 64-bit and does not need to be aligned 64/8=8 bytes

A2 takes 8+2+64, 8/2=4 does not need to be aligned, 10/64 cannot be divided into 64+64=128 bits 128/8=16 bytes

A3 occupied 8+64+2+32 8/64 cannot be divisible becomes 64+64+2+32, 128/2 is divisible does not need to be aligned, 130/32 cannot be divisible becomes 64+64+64+32=224 bits 224/8=28 bytes

If a4 has 8+64+16+64+2+128, it becomes 64+64+16+64+2+128. If A4 has 8+64+16+64+2+128, it becomes 64+64+64+64+2+128. There is no divisible 258/64 and it is changed to 64+64+64+64+64+128=448 bits. Although the String type exists, it is not aligned with 128 bits. It is still aligned with 64 bits

If you can’t divide by 8, carry

We will become a real store actual content storage area and align with memory allocation of storage space for more called expand a (this is a temporary concept, help to understand the content), will take intersection all take out the expansion of a case, if the result is empty, the system can determine the current inside the memory allocated to you you must have to use the area of less than, This area can be used to store cases

The system takes the bits from each byte that you are sure you do not need to store the cases in the enumeration. If there are not enough bits, add 1 more bytes. If the associated type is not an enumeration type, because these types are divided in bytes, the new byte is used to store the case