在掘金上看到从 汇编 到 Swift 枚举内存 的惊鸿一瞥之后,作者分析了几种不同枚举的内存布局,但是我感觉覆盖的不够全面,算是对作者那篇文章的一个补充。建议先看下作者的文章,作者的结论如下:
关联值枚举:
最大字节数之和 额外 + 1
最后一个字节 存放 case 类型
非关联值枚举:
内存 占用 1个字节
内存中 以下标数 为值,依次累加
疑问
不知道你看完之后,有没有我同样的疑问?
- 普通枚举时,内存占用一个字节,而一个字节最多只能从0到255,那么当case的选项超出256个时,会怎样
- 若关联值得类型是协议,结构体,类或其他枚举呢?这个时候内存占用是怎么样的
- 如果是递归枚举呢?
答案
- 普通枚举,测试代码和结果如下说明测试代码中的show函数会打印,枚举的地址,内存和大小,从复制Mems的
func test(){
enum TestEnum {
case testCase1
case testCase2
}
var testEnum = TestEnum.testCase1
show(val: &testEnum)
testEnum = .testCase2
show(val: &testEnum)
}
- 当case选项过多超出256个时,比如出现300个时,会占用2个字节,由于超出2个字节需要的case太多,我没有进行测试,但应该是依次类推的
//测试case过多时
func test1(){
var testEnum = MoreCaseEnum.case257
show(val: &testEnum)
}
- 当关联值是结构体时,跟作者的结论一样
struct TestStruct: TestProtocol {
var testPropetry1 = 10
var testPropetry2 = 11
var testPropetry3 = 12
var testPropetry4 = 13
var testPropetry5 = 14
}
func test2() {
enum TestStructEnum {
case testCase1
case testCase2(TestStruct)
case testCase3
}
var testEnum = TestStructEnum.testCase1
show(val: &testEnum)
testEnum = .testCase2(TestStruct())
show(val: &testEnum)
testEnum = .testCase3
show(val: &testEnum)
}
- 当关联值是class时,跟作者的结论不一样,测试代码和结果如下
结论:枚举一共占用了8个字节,若是关联class的case,则存放对象的地址,其他的按照case的顺序赋值,此时是按照2*index赋值的,index为第几个无关联值的case
//测试关联值的类型是class
func test3() {
enum TestClassEnum {
case testCase1
case testCase2(TestClass)
case testCase3
}
var testEnum = TestClassEnum.testCase1
show(val: &testEnum)
testEnum = .testCase2(TestClass())
show(val: &testEnum)
testEnum = .testCase3
show(val: &testEnum)
}
- 当关联值的类型class+bool(这里换成其他小于4个字节l的类型都一样,比如Int16,Int8)时
结论:枚举占用8字节,当关联值是对象是,存放的是对象的地址,否则,8字节的前半部分存放的是区分类型,后半部分存放的关联的值或者枚举的case的位置(具体的规则我没测出来)
func test4() {
enum TestClassOtherEnum {
case testCase1
case testCase2(TestClass)
case testCase3(Bool)
}
var testEnum = TestClassOtherEnum.testCase1
show(val: &testEnum)
testEnum = .testCase2(TestClass())
show(val: &testEnum)
testEnum = .testCase3(true)
show(val: &testEnum)
}
- 关联值的类型是占用一字节的类型时,比如bool和其他无关联值枚举
结论:枚举占用一个字节,前4位区分类型,后四位来表示具体的值
func test5() {
enum TestEnum {
case testCase1
case testCase2
}
enum TestSamllEnum {
case testCase1
case testCase2(TestEnum)
case testCase3(Bool)
}
var testEnum = TestSamllEnum.testCase1
show(val: &testEnum)
testEnum = .testCase2(.testCase2)
show(val: &testEnum)
testEnum = .testCase3(true)
show(val: &testEnum)
}
- 关联值的类型是协议时
结论:枚举占用40个字节,最后一项是区分类型,对于关联值协议的case,若满足协议的是class时,第一项是class的地址,若满足协议的是struct时,当struct的占用空间不大于24时,则前三项存放的是结构体的值,否则把结构体的值存放到外部
func test6() {
enum TestProtocolEnum {
case testCase1
case testCase2(TestProtocol)
case testCase3
}
var testEnum = TestProtocolEnum.testCase1
show(val: &testEnum)
testEnum = .testCase2(TestClass())
show(val: &testEnum)
testEnum = .testCase2(TestStruct())
show(val: &testEnum)
testEnum = .testCase3
show(val: &testEnum)
}
- 枚举类型是递归枚举时
结论:此时占用空间一直是8
func test7() {
indirect enum TestIndirectEnum {
case testCase1
case testCase2(TestIndirectEnum)
case testCase3
}
var testEnum = TestIndirectEnum.testCase1
show(val: &testEnum)
testEnum = .testCase2(.testCase3)
show(val: &testEnum)
testEnum = .testCase3
show(val: &testEnum)
}
Other
以上所有的结论都是测试并总结出来,不能保证绝对的正确性,仅供参考,测试demo