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的区别!

相关文章
相关标签/搜索