談談Swift中的枚舉內存布局

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

關聯(lián)值枚舉:
最大字節(jié)數(shù)之和 額外 + 1
最后一個字節(jié) 存放 case 類型
非關聯(lián)值枚舉:
內存 占用 1個字節(jié)
內存中 以下標數(shù) 為值,依次累加

疑問

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

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

答案

  • 普通枚舉,測試代碼和結果如下說明測試代碼中的show函數(shù)會打印,枚舉的地址,內存和大小,從復制Mems
func test(){
     enum TestEnum {
       case testCase1
       case testCase2
    }
   var testEnum = TestEnum.testCase1
   show(val: &testEnum)
   testEnum = .testCase2
   show(val: &testEnum)
}
image
  • 當case選項過多超出256個時,比如出現(xiàn)300個時,會占用2個字節(jié),由于超出2個字節(jié)需要的case太多,我沒有進行測試,但應該是依次類推的
//測試case過多時
func test1(){
    var testEnum = MoreCaseEnum.case257
    show(val: &testEnum)
}
image
  • 當關聯(lián)值是結構體時,跟作者的結論一樣
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)
}
image
  • 當關聯(lián)值是class時,跟作者的結論不一樣,測試代碼和結果如下
    結論:枚舉一共占用了8個字節(jié),若是關聯(lián)class的case,則存放對象的地址,其他的按照case的順序賦值,此時是按照2*index賦值的,index為第幾個無關聯(lián)值的case
//測試關聯(lián)值的類型是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)
}
image
  • 當關聯(lián)值的類型class+bool(這里換成其他小于4個字節(jié)l的類型都一樣,比如Int16,Int8)時
    結論:枚舉占用8字節(jié),當關聯(lián)值是對象是,存放的是對象的地址,否則,8字節(jié)的前半部分存放的是區(qū)分類型,后半部分存放的關聯(lián)的值或者枚舉的case的位置(具體的規(guī)則我沒測出來)
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)
}
image
  • 關聯(lián)值的類型是占用一字節(jié)的類型時,比如bool和其他無關聯(lián)值枚舉
    結論:枚舉占用一個字節(jié),前4位區(qū)分類型,后四位來表示具體的值
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)
}
image
  • 關聯(lián)值的類型是協(xié)議時
    結論:枚舉占用40個字節(jié),最后一項是區(qū)分類型,對于關聯(lián)值協(xié)議的case,若滿足協(xié)議的是class時,第一項是class的地址,若滿足協(xié)議的是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)
}
image
  • 枚舉類型是遞歸枚舉時
    結論:此時占用空間一直是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)
}
image

Other

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

參考鏈接

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容