Swift 枚舉-從彙編角度看枚舉內存結構

1、基本使用

先看枚舉的幾種使用(暫不要問,看看是否都能看懂,待會會逐一講解)git

一、操做一 簡單使用

//第一種方式
enum Direction { case east case west case south case north func testDir() -> String { switch self { case .east: return "東邊"
        case .west: return "西邊"
        case .south: return "南邊"
        case .north: return "北邊" } } } //第二種方式
enum Direction1 { case east, west, south, north func testDir() -> String { switch self { case .east: return "東邊"
        case .west: return "西邊"
        case .south: return "南邊"
        case .north: return "北邊" } } } var dir = Direction.east dir = .north var dir1 = Direction1.east dir1 = .north

第一種和第二種徹底同樣。github

 

二、操做二 關聯值(Associated Values)

關聯值(Associated Values)將枚舉的成員值跟其餘類型的值關聯存儲在一塊兒,很是有用!spring

2.1 關聯值示例1ide

enum Score { case points(Int) case grade(Character) } var score = Score.points(98) score = .grade("A") func testScore() { switch score { case let .points(i): print(i,"points") case let .grade(i): print("grade",i) } }

2.2 關聯值示例2工具

enum Date { case digit(year: Int, month: Int, day: Int) case string(String) } var date = Date.digit(year: 2011, month: 9, day: 10) date = .string("2011-09-10") func testDate() { switch date { case .digit(let year, let month, let day): print(year, month, day) case let .string(value): print(value) } }

2.3 關聯值示例3佈局

手機密碼方式有如下兩種,能夠用枚舉表示,用關聯值表示,圖以下:ui

上面的圖能夠用關聯值枚舉表示以下spa

enum Password { case number(Int, Int, Int, Int) case gesture(String) } var pwd = Password.number(1, 3, 7, 8) pwd = .gesture("1378") func testPwd() { switch pwd { case let .number(n1, n2, n3, n4): print("number is ", n1, n2, n3, n4) case let .gesture(str): print("gesture is " ,str) } }

 

三、操做三 原始值(Raw Values)

原始值: 枚舉成員可使用相同類型的默認值預先對應,默認值叫作原始值,原始值不佔用枚舉變量的內存3d

enum PokerSuit: Character { case spade = ""
    case heart = ""
    case diamond = ""
    case club = "" } var suit = PokerSuit.spade print(suit.rawValue)//
print(PokerSuit.club.rawValue)//

 

四、隱式原始值(Implicitly Assigned Raw Values)code

若是枚舉的原始值是Int,String,Swift會自動分配原始值,和成員值同樣

4.1 隱式原始值示例1

Direction和Direction1意義是同樣的

enum Direction: String { case north = "north"
    case south = "south"
    case east  = "east"
    case west  = "west" } enum Direction1: String { case north, south, east, west } print(Direction.north) //north
print(Direction1.north.rawValue)// north

4.2 隱式原始值示例2

Int類型,默認爲0開始

enum Season: Int { case spring, summer, autumn, winter } print(Season.spring.rawValue) // 0
print(Season.summer.rawValue) // 1
print(Season.autumn.rawValue) // 2
print(Season.winter.rawValue) //3

4.3 隱式原始值示例3

enum Season: Int { case spring = 1, summer, autumn = 4, winter } print(Season.spring.rawValue) // 1
print(Season.summer.rawValue) // 2
print(Season.autumn.rawValue) // 4
print(Season.winter.rawValue) //5

上面講述枚舉的基本使用,下面咱們將進入核心內容-從彙編的角度來看枚舉的內存!!

 

2、彙編角度看枚舉內存

示例1: 簡單實用-經過下面代碼查看枚舉實例佔用多少內存字節等

enum TestEnum { case test1, test2, test3 } var t = TestEnum.test1 t = .test2 t = .test3

經過MemoryLayout查看內存大小

enum TestEnum { case test1, test2, test3 } var t = TestEnum.test1 t = .test2 t = .test3 print(MemoryLayout<TestEnum>.size) //TestEnum實際佔用內存空間
print(MemoryLayout<TestEnum>.stride)//系統分配給TestEnum的內存空間
print(MemoryLayout<TestEnum>.alignment)//對齊參數

運行結果以下

其實Swift仍是很聰明的,僅僅使用一個字節來判斷對象的不一樣,下面窺探test1,test2,test3的內存

由於Swift不支持枚舉看底層的,因此經過一個內存訪問小工具查看內存地址,而後經過內存地址查看內存佈局

 

 拿到內存地址後,能夠經過view memory查看內容

 

將地址0x0000000100006660輸入進去

 

由於經過上面發現佔用一個字節,因此看第一個字節存儲的爲00,t爲test1時

將斷點向後移,看t = test2時,t的內存存儲的時

 

再次看下t = test2 內存存儲的值爲

 

 最後看下t = test3內存存儲爲

 

 這種形式的枚舉定義形式佔用一個字節,能夠表明的枚舉範圍也就是0x00-0xFF共256個case,足以表示全部狀況的枚舉窮舉啦!

從示例1中,當枚舉裏面僅僅是case多個對象,枚舉內存僅僅會分配1個字節來存儲各個case,case對應的爲0,1,2……

 

示例2 帶有原始值

enum TestEnum: Int { case test1 = 1, test2 = 2, test3 = 3 } var t = TestEnum.test1 t = .test2 t = .test3

觀察上面帶有原始值枚舉分配內存和佔用內存狀況

 

 從最上面講述帶有原始值的枚舉(紅色標記)原始值不佔用枚舉變量的內存

因此僅僅須要1個字節來區分test1, test2,test3,咱們再來看一個test2,看內存存儲的是多少 

 

 看出test2存儲的是依然是1,和原始值內容沒有任何關係,存儲和示例1沒有區別,再次印證了,原始值不佔用枚舉變量的內存,不影響枚舉內存結構和存儲

 

示例3 帶有關聯值的枚舉內存結構

關聯值從上面基本使用得出關聯值(Associated Values)將枚舉的成員值跟其餘類型的值關聯存儲在一塊兒

enum TestEnum { case test1(Int, Int, Int) case test2(Int, Int) case test3(Int) case test4(Bool) case test5 } var t = TestEnum.test1(1, 2, 3) t = .test2(4, 5) t = .test3(6) t = .test4(true) t = .test5

繼續使用MemoryLayout來看內存分配

 

從上面可看出TestEnum枚舉實際佔用內存空間大小爲25,又由於內存對齊爲8,因此係統分配了32個字節的大小給TestEnum

下面着重講解爲何實際佔用了25個字節,又是怎麼存儲的?

經過內存小工具查看枚舉地址

 

 而後View Memory工具查看內存結構以下

 

 上面得出Int佔據8個字節,對於TestEnum.test1(1, 2, 3)用24個字節存儲這些關聯值,得出關聯值(Associated Values)將枚舉的成員值跟其餘類型的值關聯存儲在一的結論是正確的!

(拓展:爲何01,02放在前面,爲何不是放在後面,這牽扯到大小端的問題?下面講述)

下面看test2的存儲結構t = .test2(4, 5)

 

 看第25個字節爲Test2爲0x01 = 1, test1的第25個字節爲0x00 = 0, 依次類推,查看test4應該爲3,下面揭開謎底

 

 關聯值枚舉存儲結論

有一個字節存儲成員值,用於區分哪個成員值

N個字節存儲關聯值(N取佔用內存量最大的關聯值),任何一個case的關聯值都會共用這N個字節

 

上面代碼與查看內存小工具封裝代碼https://github.com/zxy1829760/SwiftEnum

 拓展-大小端問題

 存儲0x11223344,大小端存儲以下

 以上就是枚舉內存的底層結構,但願對你們有所幫助!!! 下一篇將講述struct與class的區別!

相關文章
相關標籤/搜索