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