在上一篇关联枚举的内存布局(一)我们分析了关联类型为普通类型枚举的内存布局,这一篇我们分析一下关联类型为枚举类型的内存布局,先看一个小例子
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)
按照我们上一篇文章的分析枚举A应该占用内存大小为1+1+1=3字节,但是结果却是2字节,那么我们看一下变量a的内存布局
通过二进制来分析一下
看情况苹果对关联值是枚举类型的情况又进一步做了优化,已经不是以字节为单位来确定是否可以存储case,而是以二进制位为单位了。
一个字节可以存储256个值,枚举A的3个case占用了2个二进制位,还剩6个二进制位最多可以存储64个值,我们验证一下B中有64个case的情况,这个时候应该还是1字节,不需要+1
enum A {
enum B {
case b1
case b2
case b3
case b4
...假装有64个case
case b64
}
case a1(a:B)
case a2(a:B)
case a3(a:B)
}
var a = A.a2(a: .b64)
print(MemoryLayout<A>.size)
print(MemoryLayout<A>.stride)
print("end")
结果符合预期,看一眼a的内存布局
再验证一下枚举B中65个case的情况,根据分析应该是+1的也就是2个字节,且分别为 0x41、0x01
enum A {
enum B {
case b1
case b2
case b3
case b4
...假装有65个case
case b64
case b65
}
case a1(a:B)
case a2(a:B)
case a3(a:B)
}
var a = A.a2(a: .b65)
print(MemoryLayout<A>.size)
print(MemoryLayout<A>.stride)
print("end")
字节数+1了,符合预期,看一眼a的内存布局
这一步不符合预期,也就是苹果并没有以字节为单位拿来存储A中的case,通过二进制来分析一下
继续验证
enum A {
enum B {
case b1
case b2
case b3
case b4
...假装有65个case
case b64
case b65
}
case a1(a:B)
case a2(a:B)
case a3(a:B)
}
var a = A.a3(a: .b65)
print(MemoryLayout<A>.size)
print(MemoryLayout<A>.stride)
print("end")
这个时候取a = A.a3的值,那么a的内存布局应该是
验证一下
符合预期!!
以上我们分析了枚举A中的case只有一个参数的情况,那么如果有多个枚举B类型参数的情况呢??
enum A {
enum B {
case b1
case b2
case b3
case b4
...假装有65个case
case b64
case b65
}
case a1(a:B,b:B)
case a2(a:B,b:B)
case a3(a:B,b:B)
}
var a = A.a2(a: .b65,b: .b65)
print(MemoryLayout<A>.size)
print(MemoryLayout<A>.stride)
print("end")
这时A.a3的参数a占用了7个二进制位,参数b同样占用了7个二进制位,那么这两个字节分别可以提供1个二进制位给A拿来存储case
如果两个参数不是同一个类型呢?
enum A {
enum B {
case b1
...假装有65个case
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")
这种情况比较特殊,第二个参数都是枚举C类型的,占用3个二进制位,如果是以下这种情况呢
enum A {
enum B {
case b1
...假装有65个case
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:B) //这里第二个参数改为枚举B类型
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")
enum A {
enum B {
case b1
...假装有65个case
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")
总结:
计算枚举中case大小的规则:前面n-1个参数所占二进制位的和要为第n个参数所占用二进制位或64位的最小正整数倍
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)
a1为64位不需要对齐 64/8=8字节
a2占用8+2+64,8/2=4不需要对齐,10/64无法整除则变为64+64=128位 128/8=16字节
a3占用8+64+2+32 8/64无法整除变为64+64+2+32,128/2可整除不需要对齐,130/32无法整除变为64+64+64+32=224位 224/8=28字节
a4占用8+64+16+64+2+128 8/64无法整除变为64+64+16+64+2+128,128/16可整除不需要对齐,144/64无法整除变为64+64+64+64+2+128, 258/64无法整除变为64+64+64+64+64+128=448位,虽然有String类型存在,但是这里不以128位对齐,仍然以64位对齐 448/8=56字节