enum Direction_1 {
case north, south, east, west
}
enum Direction {
case north
case south
case east
case west
}
var dir = Direction.west
dir = Direction.east
dir = .north
print(dir)
switch dir {
case .north:
print("north")
case .south:
print("south")
case .east:
print("east")
case .west:
print("west")
}
复制代码
关联值是直接存在枚举变量的内存里面的,这点要牢记
,对于一个有固定取值范围的变量,设计成枚举比较合适git
enum Score {
case points(Int)
case grade(Character)
}
var score = Score.points(96)
score = .grade("A")
switch score {
case let .points(i):
print(i, "points")
case let .grade(i):
print("grade", i)
} // grade A
enum Date {
case digit(year: Int, month: Int, day: Int)
case string(String)
}
var date = Date.digit(year: 2020, month: 02, day: 29)
date = .string("2020-02-29")
switch date {
case let .digit(year, month, day):
print(year, month, day)
case let .string(dateStr):
print(dateStr)
} // "2020-02-29"
复制代码
注意上看switch内部对
let/var
关键字的使用,若是下载枚举值左边,那么关联值只能统一绑定给let常量
或者var变量
github
case let .digit(year, month, day): //year、month、day都是let常量
case var .digit(year, month, day): //year、month、day都是var变量
复制代码
若是let/var关键字写在关联值括号内,就比较灵活spring
case .digit(let year, var month, let day)
复制代码
另一些枚举举例swift
enum Password {
case number(Int, Int, Int, Int)
case gesture(String)
}
var pwd = Password.number(3, 5, 7, 9)
pwd = .gesture("3259")
switch pwd {
case let .number(n1 , n2 , n3 , n4 ): //数字密码
print("number is", n1, n2, n3, n4)
case let .gesture(pwdStr):// 字符串密码
print("gestrue is", pwdStr)
}
复制代码
枚举成员能够只用相同类型的默认值预先关联,这个默认值叫作 原始值markdown
enum PokerSuit: Character { //这里的Character表示的是枚举值所关联的原始值
case spade = "️"
case heart = "️"
case diamond = "️"
case club = "️"
}
var suit = PokerSuit.spade
print(suit)
print(suit.rawValue)
print(PokerSuit.club.rawValue)
enum Grade: String {
case perfect = "A"
case great = "B"
case good = "C"
case bad = "D"
}
print(Grade.perfect.rawValue) // A
print(Grade.great.rawValue) // B
print(Grade.good.rawValue) // C
print(Grade.bad.rawValue) // D
复制代码
enum Direction1: String {
case north, south, east, west
}
print(Direction1.north.rawValue)
enum Direction2: String {
case north = "nor", south, east, west
}
print(Direction2.north.rawValue)//有赋值,就用赋值的字符串
print(Direction2.south.rawValue)//没赋值, 就用case名字符串
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
enum Season2: Int {
case spring = 2, summer, autumn = 6, winter
}
print(Season2.spring.rawValue) //2
print(Season2.summer.rawValue) //3
print(Season2.autumn.rawValue) //6
print(Season2.winter.rawValue) //7
复制代码
//书写方法一
indirect enum ArithExpr_1 {
case number(Int)
case sum(ArithExpr, ArithExpr)
case difference(ArithExpr, ArithExpr)
}
//书写方法二
enum ArithExpr {
case number(Int)
indirect case sum(ArithExpr, ArithExpr)
indirect case difference(ArithExpr, ArithExpr)
}
let five = ArithExpr.number(5)
let four = ArithExpr.number(4)
let two = ArithExpr.number(2)
let sum = ArithExpr.sum(five, four)
let difference = ArithExpr.difference(sum, two)
func calculate(_ expr: ArithExpr) -> Int{
switch expr {
case let .number(value):
return value
case let .sum(left, right):
return calculate(left) + calculate(right)
case let .difference(left, right):
return calculate(left) - calculate(right)
}
}
复制代码
咱们可使用
MemoryLayout
来获取数据类型占用的内存大小,至关于C里面使用的sizeof
ide
enum Password2 {
case number(Int, Int, Int, Int)
case other
}
MemoryLayout<Password2>.stride //系统分配给变量的内存大小--40
MemoryLayout<Password2>.size //实际被使用的内存大小--33
MemoryLayout<Password2>.alignment //对其参数--8
var pd = Password2.number(9, 8, 7, 6)
pd = .other
print(pd) //"other/n"
MemoryLayout.stride(ofValue: pd) //40
MemoryLayout.size(ofValue: pd) //33
MemoryLayout.alignment(ofValue: pd) //8
复制代码
经过MemoryLayout,咱们只能简单查看一些内存相关的信息,但还不足以看清枚举在内存中的具体细节,因为Xcode调试工具没法为咱们提供枚举变量的内存地址,所以须要借助一些额外的工具,这里推介一下大牛李明杰的一个工具。工具
(1)首先来看下一种简单的状况~~~~~~~oop
enum TestEnum {
case test1, test2, test3
}
print("系统实际分配内存",MemoryLayout<TestEnum>.stride)
print("实际使用的内存",MemoryLayout<TestEnum>.size)
print("内存对齐参数",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test1
print("枚举变量t的内存地址:",Mems.ptr(ofVal: &t)) //这里能够输出变量t的内存地址
t = .test2
t = .test3
print("Stop for debug")
复制代码
Mems.ptr(ofVal: &t)
能够帮咱们得到变量t的内存地址,准备好3个断点 而后将程序运行值
断点1
处,此时咱们已经得到t
的内存地址,根据该地址,调出内存界面,咱们来观察一下此时的内存细节 在继续走到断点二、断点3处,对比一下各自的内存状况以下
ui
小结:enum TestEnum { case test1, test2, test3 }
spa
- 系统为TestEnum类型的变量分配
1个字节
的内存空间- test1 、 test二、 test3 三个case对应在内存中用整数0、一、2来表示
(2)把场景调整为有Int
型原始值的情形以下~~~~~~~
enum TestEnum: Int {
case test1
case test2 = 3
case test3
case test4 = 10
case test5
}
print("系统实际分配内存",MemoryLayout<TestEnum>.stride)
print("实际使用的内存",MemoryLayout<TestEnum>.size)
print("内存对齐参数",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test1
print("枚举变量t的内存地址:",Mems.ptr(ofVal: &t)) //这里能够输出变量t的内存地址
t = .test2
t = .test3
t = .test4
t = .test5
print("Stop for debug")
复制代码
按照上面一样的方法,对比各自case
的内存状况以下
咱们在查看一下各自
case
的rawValue
print("test1的rawValue:", TestEnum.test1.rawValue)
print("test2的rawValue:", TestEnum.test2.rawValue)
print("test2的rawValue:", TestEnum.test3.rawValue)
print("test2的rawValue:", TestEnum.test4.rawValue)
print("test2的rawValue:", TestEnum.test5.rawValue)
***********运行结果
test1的rawValue: 0
test2的rawValue: 3
test2的rawValue: 4
test2的rawValue: 10
test2的rawValue: 11
复制代码
看得出,若是原始值类型为Int
:
case
的原始值默为整数0
,非首个case
的默认值为上一个case
的默认值+1
(3)看过了带Int
型原始值的状况以后,在看一下带String
型原始值的状况,改造以下~~~~~~~
enum TestEnum: String {
case test1
case test2 = "AA"
case test3 = "汉字"
case test4 = "🦕"
case test5
}
print("系统实际分配内存",MemoryLayout<TestEnum>.stride)
print("实际使用的内存",MemoryLayout<TestEnum>.size)
print("内存对齐参数",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test1
print("枚举变量t的内存地址:",Mems.ptr(ofVal: &t)) //这里能够输出变量t的内存地址
t = .test2
t = .test3
t = .test4
t = .test5
print("Stop for debug")
print("test1的rawValue:", TestEnum.test1.rawValue)
print("test2的rawValue:", TestEnum.test2.rawValue)
print("test2的rawValue:", TestEnum.test3.rawValue)
print("test2的rawValue:", TestEnum.test4.rawValue)
print("test2的rawValue:", TestEnum.test5.rawValue)
****************运行结果
系统实际分配内存 1
实际使用的内存 1
内存对齐参数 1
枚举变量t的内存地址: 0x0000000100008218
Stop for debug
test1的rawValue: test1
test2的rawValue: AA
test2的rawValue: 汉字
test2的rawValue: 🦕
test2的rawValue: test5
Program ended with exit code: 0
复制代码
内存的状况这里省略,和上面Int
型的时候是同样的,根据调试输出的状况,咱们能够看出
case
的原始值为该case名称
的字符串case
的原始值即为设定值
总结 带原始值的枚举
- 枚举变量自己的就占一个字节
- 枚举变量所对应的内存里所存放的具体值:对应第一个case为0,而且日后逐个+1
(4)带关联值的场景~~~~~~~
enum TestEnum {
case test1(a: Int, b: Int, c: Int)
case test2(d: Int, e: Int)
case test3(f: Int)
case test4(g: Bool)
case test5
}
print("系统实际分配内存",MemoryLayout<TestEnum>.stride)
print("实际使用的内存",MemoryLayout<TestEnum>.size)
print("内存对齐参数",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test1(a: 1, b: 2, c: 3)
//这里能够输出变量t的内存地址
print("枚举变量t的内存地址:",Mems.ptr(ofVal: &t))
t = .test2(d: 4, e: 5)
t = .test3(f: 6)
t = .test4(g: true)
t = .test5
print("Stop for debug")
*****************运行结果
系统实际分配内存 32
实际使用的内存 25
内存对齐参数 8
枚举变量t的内存地址: 0x0000000100008208
复制代码
接下来照例在过一遍内存,下面直接贴上内存查看的结果
t = test1(a: 1, b: 2, c: 3)
01 00 00 00 00 00 00 00 --> 对应a
02 00 00 00 00 00 00 00 --> 对应b
03 00 00 00 00 00 00 00 --> 对应c
00 00 00 00 00 00 00 00 --> 对应case test1
复制代码
t = test2(d: 4, e: 5)
04 00 00 00 00 00 00 00 --> 对应d
05 00 00 00 00 00 00 00 --> 对应e
00 00 00 00 00 00 00 00 --> 此时没用到
01 00 00 00 00 00 00 00 --> 对应case test2
复制代码
t = test3(f: 6)
06 00 00 00 00 00 00 00 --> 对应f
00 00 00 00 00 00 00 00 --> 此时没用到
00 00 00 00 00 00 00 00 --> 此时没用到
02 00 00 00 00 00 00 00 --> 对应case test3
复制代码
t = test4(g: true)
01 00 00 00 00 00 00 00 --> 对应g
00 00 00 00 00 00 00 00 --> 此时没用到
00 00 00 00 00 00 00 00 --> 此时没用到
03 00 00 00 00 00 00 00 --> 对应case test4
复制代码
t = test5
00 00 00 00 00 00 00 00 --> 此时没用到
00 00 00 00 00 00 00 00 --> 此时没用到
00 00 00 00 00 00 00 00 --> 此时没用到
04 00 00 00 00 00 00 00 --> 对应case test5
复制代码
总结 带关联值的枚举
- 枚举变量的
成员case
的值只用了其内存空间的1字节来存放- 枚举的
case关联值
也存放在枚举变量的内存中- 系统为枚举的
case关联值
所分配的内存空间,必须保证能够放下所需内存最大的那个关联值- 枚举变量的内存空间里,先存放存放的是
case关联值
,成员case
的值被放在最后- 枚举变量的内存总空间按内存对齐参数进行补齐(计算机常识)
(5)一些极端场景~~~~~~~
enum TestEnum {
case test
}
print("系统实际分配内存",MemoryLayout<TestEnum>.stride)
print("实际使用的内存",MemoryLayout<TestEnum>.size)
print("内存对齐参数",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test
print(print("枚举变量t的内存地址:",Mems.ptr(ofVal: &t)))
****************运行结果
系统实际分配内存 1
实际使用的内存 0
内存对齐参数 1
枚举变量t的内存地址: 0x0000000000000001
Program ended with exit code: 0
复制代码
能够看到,系统确实是分配了1
个字节给枚举,可是实际上用到了0
个,由于一种状况不须要作任何区分,因此也就不须要存储,固然貌似没人会这么用,因此系统针对这种状况下的处理,就不难理解了。在看看带关联值的状况:
enum TestEnum {
case test(Int)
}
print("系统实际分配内存",MemoryLayout<TestEnum>.stride)
print("实际使用的内存",MemoryLayout<TestEnum>.size)
print("内存对齐参数",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test(10)
print("枚举变量t的内存地址:",Mems.ptr(ofVal: &t))
print("Stop for debug")
***************运行结果
系统实际分配内存 8
实际使用的内存 8
内存对齐参数 8
枚举变量t的内存地址: 0x0000000100007200
Stop for debug
Program ended with exit code: 0
***************汇编结果
0A 00 00 00 00 00 00 00
复制代码
能够看到系统直接分配了8
个字节来存储枚举里面的Int
型关联值,没有分配空间来存储成员case
的值,缘由和上面很想,由于如今就是一种case
,没有必要再存储成员变量的值,只须要关心case关联值
就好。那若是有一个以上的case,是否是就会给成员case
分配空间了?我们试试看,以下
enum TestEnum {
case other
case test(Int)
}
print("系统实际分配内存",MemoryLayout<TestEnum>.stride)
print("实际使用的内存",MemoryLayout<TestEnum>.size)
print("内存对齐参数",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.other
//Mem.memStr是大神李明杰提供的工具,文中有连接,能够帮我直接获取变量的内存里面的值
print("枚举变量t = other 时的内存状况: ",Mems.memStr(ofVal: &t))
t = TestEnum.test(10)
print("枚举变量t = test(10)时的内存地址:",Mems.memStr(ofVal: &t))
print("Stop for debug")
***************运行结果
系统实际分配内存 16
实际使用的内存 9
内存对齐参数 8
枚举变量t = other 时的内存状况: 0x0000000000000000 0x0000000000000001
枚举变量t = test(10)时的内存地址: 0x000000000000000a 0x0000000000000000
复制代码
能够看出,只要case
大于1
个,除了Int
型关联值须要占用8
个字节外,枚举变量还使用了1
个字节来存储成员case
的值,根据内存对齐参数8
,系统给枚举变量分配了16
字节空间。上面的结果中,最后一个字节是用来存放成员case
的值也就是case other
对应了01
, case test
对应了00
,可是感受顺序不太对,明明是other
在前,test
在后的,带着这个疑问,咱们把用例改造以下
enum TestEnum {
case aaa
case test(Int)
case ccc
case test3(Int, Int)
case test2(Int,Int, Int)
case other
}
print("系统实际分配内存",MemoryLayout<TestEnum>.stride)
print("实际使用的内存",MemoryLayout<TestEnum>.size)
print("内存对齐参数",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.aaa
print("t = .aaa的内存状况: ",Mems.memStr(ofVal: &t))
t = TestEnum.test(10)
print("t = .test(10)的内存状况: ",Mems.memStr(ofVal: &t))
t = TestEnum.ccc
print("t = .ccc的内存状况: ",Mems.memStr(ofVal: &t))
t = TestEnum.test3(16, 32)
print("t = .test3(16, 32)的内存状况: ",Mems.memStr(ofVal: &t))
t = TestEnum.test2(20, 20, 20)
print("t = .test2(20, 20, 20)的内存状况:",Mems.memStr(ofVal: &t))
t = TestEnum.other
print("t = .other的内存状况: ",Mems.memStr(ofVal: &t))
print("Stop for debug")
*************************************运行结果
系统实际分配内存 32
实际使用的内存 25
内存对齐参数 8
t = .aaa的内存状况: 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000003
t = .test(10)的内存状况: 0x000000000000000a 0x0000000000000000 0x0000000000000000 0x0000000000000000
t = .ccc的内存状况: 0x0000000000000001 0x0000000000000000 0x0000000000000000 0x0000000000000003
t = .test3(16, 32)的内存状况: 0x0000000000000010 0x0000000000000020 0x0000000000000000 0x0000000000000001
t = .test2(20, 20, 20)的内存状况: 0x0000000000000014 0x0000000000000014 0x0000000000000014 0x0000000000000002
t = .other的内存状况: 0x0000000000000002 0x0000000000000000 0x0000000000000000 0x0000000000000003
Stop for debug
Program ended with exit code: 0
复制代码
从上面的调试,又挖掘了一点小细节:
成员case
,它的case
值会根据定义的顺序,默认从0开始+1累加,成员case
,它们的case
值相同,并且都等于最后一个可关联成员case 的值+1
以上咱们看清楚了简单枚举、关联值枚举、原始值枚举在内存中分别是如何存储的,能够看出,枚举的关联值和原始值又如下区别:
xx.rawValue
访问的,所以它的值彻底不须要存储,能够在枚举定义完以后经过方法提供给外部。enum Direction
: String/Int/...
{...}
。关联值则必须在枚举定义的时候,肯定好case
所对应的关联值类型case
被赋值给变量的时候进行赋值,由于同一个case
每次被赋值给变量,都须要设定一个关联值,所以也能够说关联值是能够改变的,以下enum Score {
case points(Int)
case grade(Character)
}
var score = Score.points(96)
score = .grade("A")
score = .grade("B") -->相同的case,不一样的关联值
复制代码
而原始值,只能在枚举定义的时候进行赋值,不赋值则系统会给定相应的默认值,也就是只有一次机会能够赋值,定义完枚举以后,就没有办法能够更改原始值了,示例以下
enum Grade: String {
case perfect = "A"
case great
case good = "C"
case bad = "D"
}
print(Grade.perfect.rawValue) --> A
print(Grade.great.rawValue) --> 定义时无赋值,系统默认为case的名称 great
print(Grade.good.rawValue) --> C
print(Grade.bad.rawValue) -> D
复制代码
switch
的实现原理(待续...)