Swift 枚举-从汇编角度看枚举内存结构
本人已迁移博客至掘进,以后会在掘进平台更新最新的文章也会有更多的干货,欢迎大家关注!!!https://juejin.im/user/588993965333309
一、基本使用
先看枚举的几种使用(暂不要问,看看是否都能看懂,待会会逐一讲解)
1、操作一 简单使用
//第一种方式 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
第一种和第二种完全一样。
2、操作二 关联值(Associated Values)
关联值(Associated Values)将枚举的成员值跟其他类型的值关联存储在一起,非常有用!
2.1 关联值示例1
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
手机密码方式有以下两种,可以用枚举表示,用关联值表示,图如下:
上面的图可以用关联值枚举表示如下
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) } }
3、操作三 原始值(Raw Values)
原始值: 枚举成员可以使用相同类型的默认值预先对应,默认值叫做原始值,原始值不占用枚举变量的内存
enum PokerSuit: Character { case spade = "黑" case heart = "红" case diamond = "方" case club = "花" } var suit = PokerSuit.spade print(suit.rawValue)//黑 print(PokerSuit.club.rawValue)//花
4、隐式原始值(Implicitly Assigned Raw Values)
如果枚举的原始值是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
上面讲述枚举的基本使用,下面我们将进入核心内容-从汇编的角度来看枚举的内存!!
二、汇编角度看枚举内存
示例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的区别!