談談Swift中的枚舉內存佈局

NO IMAGE

在掘金上看到從 彙編 到 Swift 枚舉內存 的驚鴻一瞥之後,作者分析了幾種不同枚舉的內存佈局,但是我感覺覆蓋的不夠全面,算是對作者那篇文章的一個補充。建議先看下作者的文章,作者的結論如下:

關聯值枚舉:
最大字節數之和 額外 + 1
最後一個字節 存放 case 類型
非關聯值枚舉:
內存 佔用 1個字節
內存中 以下標數 為值,依次累加

疑問

不知道你看完之後,有沒有我同樣的疑問?

  1. 普通枚舉時,內存佔用一個字節,而一個字節最多隻能從0到255,那麼當case的選項超出256個時,會怎樣
  2. 若關聯值得類型是協議,結構體,類或其他枚舉呢?這個時候內存佔用是怎麼樣的
  3. 如果是遞歸枚舉呢?

答案

  • 普通枚舉,測試代碼和結果如下說明測試代碼中的show函數會打印,枚舉的地址,內存和大小,從複製Mems
func test(){
enum TestEnum {
case testCase1
case testCase2
}
var testEnum = TestEnum.testCase1
show(val: &testEnum)
testEnum = .testCase2
show(val: &testEnum)
}

談談Swift中的枚舉內存佈局

  • 當case選項過多超出256個時,比如出現300個時,會佔用2個字節,由於超出2個字節需要的case太多,我沒有進行測試,但應該是依次類推的
//測試case過多時
func test1(){
var testEnum = MoreCaseEnum.case257
show(val: &testEnum)
}

談談Swift中的枚舉內存佈局

  • 當關聯值是結構體時,跟作者的結論一樣
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)
}

談談Swift中的枚舉內存佈局

  • 當關聯值是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)
}

談談Swift中的枚舉內存佈局

  • 當關聯值的類型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)
}

談談Swift中的枚舉內存佈局

  • 關聯值的類型是佔用一字節的類型時,比如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)
}

談談Swift中的枚舉內存佈局

  • 關聯值的類型是協議時
    結論:枚舉佔用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)
}

談談Swift中的枚舉內存佈局

  • 枚舉類型是遞歸枚舉時
    結論:此時佔用空間一直是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)
}

談談Swift中的枚舉內存佈局

Other

以上所有的結論都是測試並總結出來,不能保證絕對的正確性,僅供參考,測試demo

參考鏈接

相關文章

前端利用過渡動效打造沉浸式的體驗

面試官:如果http響應頭中ETag值改變了,是否意味著文件內容一定已經更改

一文解析RabbitMQ最常用的三大模式

通過定時器、時間分片、WebWorker優化長任務