1:枚举
enum Direction {
case north
case south
case east
case west
}
enum DirectionNew {
case north, south, east, west
}
var dir = Direction.west
dir = Direction.east
dir = .north
print(dir) // north
switch dir {
case .north:
print("north")
case .south:
print("south")
case .east:
print("east")
case .west:
print("west")
}
关联值
有事会将枚举的成员值跟其他类型的关联存储在一起,会非常有用。下面必要时let也可以改为var
关联值是直接存在枚举变量的内存里面的,这点要牢记
,对于一个有固定取值范围的变量,设计成枚举比较合适
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, mouth: Int, day: Int)
case string(String)
}
var date = Date.digit(year: 2011, mouth: 9, day: 10)
date = .string("2011-9-10")
switch date {
case .digit(year: let year, mouth: let mouth, day: let day):
print(year, mouth, day)
case let .string(value):
print(value)
}
关联值举例
原始值:Raw Values
枚举成员可以使用相同类型的默认值预先关联,这个默认值叫做原始值
隐式原始值
如果枚举的原始值类型是Int、String。Swift会自动分配原始值。 有赋值的取值的话先取赋值的字段
enum Direction : String {
case north = "north"
case south = "south"
case east = "east"
case west = "west"
}
enum DirectionNew : String {
case north, south, east, west
}
print(DirectionNew.north) // north
print(DirectionNew.north.rawValue) // north
enum Season: Int {
case sping, summer, autumn, winter
}
print(Season.sping.rawValue) // 0
print(Season.summer.rawValue) // 1
print(Season.autumn.rawValue) // 2
print(Season.winter.rawValue) // 3
enum SeasonNew: Int {
case sping = 1, summer, autumn = 4, winter
}
print(SeasonNew.sping.rawValue) // 1
print(SeasonNew.summer.rawValue) // 2
print(SeasonNew.autumn.rawValue) // 4
print(SeasonNew.winter.rawValue) // 5
枚举递归(Recursive Enumeration)
//书写方法一
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
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
mutating
结构体和枚举是值类型,默认情况下,值类型的属性不能被自身的实例方法修改 在func关键字前加mutating可以运行这种修改行为
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(delayX: Double, delayY: Double) {
x += delayX
y += delayY
// self = Point(x: x + delayX, y: y + delayY)
}
}
enum StateSwitch {
case low, middle, high
mutating func next() {
switch self {
case .low:
self = .middle
case .middle:
self = .high
case .high:
self = .low
}
}
}
枚举在内存中是如何存储的?下面这转载自
通过MemoryLayout,我们只能简单查看一些内存相关的信息,但还不足以看清枚举在内存中的具体细节,由于Xcode调试工具无法为我们提供枚举变量的内存地址,因此需要借助一些额外的工具,这里推介一下大牛李明杰的一个工具。
(1)首先来看下一种简单的情况~~~~~~~
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
的内存地址,根据该地址,调出内存界面,我们来观察一下此时的内存细节
在继续走到断点2、断点3处,对比一下各自的内存情况如下
小结:enum TestEnum { case test1, test2, test3 }
- 系统为TestEnum类型的变量分配
1个字节
的内存空间- test1 、 test2、 test3 三个case对应在内存中用整数0、1、2来表示
(2)把场景调整为有Int
型原始值的情形如下~~~~~~~
总结 单层枚举:不是带关联值的枚举
- 枚举变量本身的就占一个字节,这个字节的初始值为0,也就是成员存储值,如果内部有值则按照+1顺序顺延
- 枚举变量内部进行的赋值234等这些,是不存储在枚举内部。它可以是不占枚举内存空间的,可以是if的形式表达出来的
(4)带关联值的场景
总结 带关联值的枚举
1个字节存储成员值,存储成员值的前提是有多个case
在枚举变量的内存中先存放的是枚举的case关联值,也就是赋值的值,然后再存放的是成员存储值,这个值可以表示这个内存是第几个成员,上图中画红色的线的部分代表了0、1、2、3、4个成员存储值的顺序
Swift会根据所需内存空间最大的case为枚举变量分配内存,同时再多给出一个字节作为标志用于区分
枚举变量的内存总空间按内存对齐参数进行补齐(计算机常识)
5)一些极端场景
那如果有一个以上的case,是不是就会给成员case
分配空间了?咱们试试看,如下
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
关联值 VS 原始值rawValue
以上我们看清楚了简单枚举、关联值枚举、原始值枚举在内存中分别是如何存储的,可以看出,枚举的关联值和原始值又以下区别:
内存角度:关联值是直接存储在枚举变量内存里面的,而原始值则不是,因为原始值是通过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
值类型是不允许在内部修改其变量的值,但是可以设置设置初始值,刚开始,系统会自动为我们创建()的方法