前言:
Swift语言中,具有类特征
的类型包括三种,即枚举类型
、结构类型
(包括基本类型,基本类型实际都是结构类型的特例)、类
。其中枚举类型
、结构类型
是属于值类型
,类
属于引用类型
。三种类型都可以添加属性、方法、下标方法,能够使用扩展进行功能扩,使用协议等.下面就分享下Swift中的枚举类型.
C语⾔枚举的写法回顾
在将 Swift 的枚举之前,我们先来回顾⼀下 C 语⾔的枚举写法:
enum 枚举名{
枚举值1,
枚举值2,
......
};
<!--举例:表示一周7天-->
enum Weak{
MON, TUE, WED, THU, FRI, SAT, SUN
};
<!--更改C中枚举默认值-->
//如果没有设置枚举默认值,一般第一个枚举成员的默认值为整型0,后面依次递推
enum Weak{
MON = 1, TUE, WED, THU, FRI, SAT, SUN
};
<!--C中定义一个枚举变量-->
//表明创建了一个枚举,并声明了一个枚举变量weak
enum Weak{
MON = 1, TUE, WED, THU, FRI, SAT, SUN
}weak;
//或者下面这种写法,省略枚举名称
enum{
MON = 1, TUE, WED, THU, FRI, SAT, SUN
}weak;
Swift 中枚举写法类⽐
接下来回到 Swift 枚举中,我们思考⼀个例⼦:如果说我们要定⼀个颜⾊的枚举 LGColor ,在
Swift 中应该怎么写
enum Weak { case MONDAY
case TUEDAY
case WEDDAY
case THUDAY
case FRIDAY
case SATDAY
case SUNDAY
}
<!----------也可以这样写----------->
enum Weak {
case MON, TUE, WED, THU, FRI, SAT, SUN
}
上述代码中我们的枚举值默认是整形
,这个 C 是⼀致的,如果我们想要表达的 String
怎么办?
enum Weak: String {
case MON = "MON"
case TUE = "TUE"
case WED = "WED"
case THU = "THU"
case FRI = "FRI"
case SAT = "SAT"
case SUN = "SUN"
}
<!-------隐士的书写方法------->
enum Weak: Int {
mon, tue, wed, thu, fri = 10, sat, sun
}
<!-------String类型---------->
enum Weak: String {
case mon, tue, wed, thu, fri = "Hello", sat, sun
}
print(Weak.mon.rawValue)
print(Weak.fri.rawValue)
=
号左边的值在 Swift 中我们把他叫做 RawValue
,如果我们不想写后⾯的字符串,这个时候我们就可以使⽤ `隐⼠ RawValue 分配
`
枚举的遍历
Swift中的枚举能像集合一样进行遍历
enum Weak: String {
case mon, tue, wed, thu, fri = "Hello", sat, sun
}
extension Weak: CaseIterable{}
var allCase = Weak.allCases
<!-------方式1:for循环------->
for c in allCase{
print(c)
}
<!-----打印结果------>
mon
tue
wed
thu
fri
sat
sun
<!--方式2:通过函数式编程遍历-->
let allCase = Weak.allCases.map({"\($0)"}).joined(separator: ", ")
print(allCase)
<!---打印结果---->
mon, tue, wed, thu, fri, sat, sun
CaseIterable协议
通常用于没有关联值的枚举
,用来访问所有的枚举值
,只需要对应的枚举遵守该协议即可,然后通过allCases
获取所有枚举值
枚举关联值
如果希望用枚举表示复杂的含义,关联更多的信息,就需要使用关联值了
例如,使用enum表达一个形状,其中有圆形、长方形等,圆形有半径,长方形有宽、高,我们可以通过下面具有关联值的enum来表示
enum Shape{
//case枚举值后括号内的就是关联值,例如 radius
case circle(radius: Double)
case rectangle(width: Int, height: Int)
}
<!-----使用----->
<!--创建-->
var shape = Shape.circle(radius: 10.0)
<!--重新分配-->
shape = Shape.rectangle(width: 10, height: 10)
// 使用关联值
switch shape {
case .circle(let radius):
print("面积:\(radius*radius)")
case .rectangle(let with,let height)
print("面积:\(with*height)")
}
// 使用关联值
switch shape {
case let .circle(radius):
print("面积:\(radius*radius)")
case let .rectangle(with,height)
print("面积:\(with*height)")
}
// 判断s是否是square类型。并获取`关联内容`
// 1. 内部声明关联内容类型(如: let)
if case .circle(let width) = s {
print(width)
}
// 2. 声明case所有关联内容类型(如: var)
if case var .rectangle(radius, borderWidth) = c {
radius += 200
borderWidth += 100
print(radius, borderWidth)
}
具有关联值
的枚举,就没有rawValue属性
了,主要是因为一个case可以用一个或者多个值
来表示,而rawValue
只有单个
的值
枚举的嵌套
枚举的嵌套主要用于以下场景:
-
一个复杂枚举是由
一个
或多个枚举
组成(枚举嵌套枚举
) -
enum是
不对外公开
的,即是私有
的(结构体嵌套枚举
)
枚举嵌套枚举
例如:游戏中的方向键为例,有上下左右四个方向键,不同的组合会沿着不同的方向前进
enum CombineDirect{
//枚举中嵌套的枚举
enum BaseDirect{
case up
case down
case left
case right
}
//通过内部枚举组合的枚举值
case leftUp(baseDIrect1: BaseDirect, baseDirect2: BaseDirect)
case leftDown(baseDIrect1: BaseDirect, baseDirect2: BaseDirect)
case rightUp(baseDIrect1: BaseDirect, baseDirect2: BaseDirect)
case rightDown(baseDIrect1: BaseDirect, baseDirect2: BaseDirect)
}
//使用
let leftUp = CombineDirect.leftUp(baseDIrect1: CombineDirect.BaseDirect.left, baseDirect2: CombineDirect.BaseDirect.up)
结构体嵌套枚举
//结构体嵌套枚举
struct Skill {
enum KeyType{
case up
case down
case left
case right
}
let key: KeyType
func launchSkill(){
switch key {
case .left, .right:
print("left, right")
case .up, .down:
print("up, down")
}
}
}
枚举中包含属性
enum中只能包含计算属性、类型属性,不能包含存储属性
enum Shape{
case circle(radius: Double)
case rectangle(width: Double, height: Double)
//编译器报错:Enums must not contain stored properties 不能包含存储属性,因为enum本身是值类型
// var radius: Double
//计算属性 - 本质是方法(get、set方法)
var with: Double{
get{
return 10.0
}
}
//类型属性 - 是一个全局变量
static let height = 20.0
}
枚举中包含方法
可以在enum中定义实例方法、static修饰的方法
enum Weak: Int{
case MON, TUE, WED, THU, FRI, SAT, SUN
mutating func nextDay(){
if self == .SUN{
self = Weak(rawValue: 0)!
}else{
self = Weak(rawValue: self.rawValue+1)!
}
}
}
<!--使用-->
var w = Weak.MON
w.nextDay()
print(w)
枚举的SIL分析
sil分析
以DayOfWeak枚举为例,查看枚举编译和取值的过程
enum DayOfWeak:String {
case monday ,tuesday ,wednesday = "Hello" ,thursday ,friday ,saturday ,sunday
}
print(DayOfWeak.monday.rawValue)
print (DayOfWeak.wednesday.rawValue)
<!-------打印结果----->
monday
Hello
进入SIR文件查看下枚举具体的编译
输出当前 case 的 RawValue 发⽣了什么?
查看getter方法
使用rawValue
的本质是调用get
方法,但是get
方法中的String
是从哪里来的呢?String存储
在哪里?其实这些对应分支的字符串在编译时期就已经存储好了,即存放在Maach-O
文件的__TEXT.cstring
中,且是连续的内存空间,可以通过编译后查看Mach-O文件来验证
是从Mach-O
文件对应地址取出的字符串
,然后再返回给w
枚举值和 RawValue 的区别
从以下案例分析枚举值和RawValue的区别
enum DayOfWeak:String {
case monday ,tuesday ,wednesday = "Hello" ,thursday ,friday ,saturday ,sunday
}
print (DayOfWeak.monday)
print(DayOfWeak.monday.rawValue)
<!------打印结果----->
monday
monday
以上打印的都是“monday”
,是不是意味着枚举值
和RawValue
是同一个东西?并不是
,第一个输出的输出的case枚举值
,第二个输出的是通过rawValue
访问的rawValue
的get
方法
举个例子定义一个String类型的变量,并赋值
所以上⾯ print
出来的就是具体的枚举值
,⽽通过 rawValue
访问的就是 rawValue
的 get
⽅法
那如何访问enum的枚举值呢?
SIR文件分析
初始化返回Optional
类型,DayOfWeak赋值调用%19
index_addr
其实就是去当前数组
中取第 n 个元素
的值的地址
,然后在把构建好的字符串放到当前地址中。所以可以看到,当前我们指定 enum 的 RawValueType
为 String
之后,系统⾃动创建了⼀块连续的内存空间
来默认存放当前 case 对应的字符串
。
继续分支跳转
以上就是枚举匹配的流程
补充:
_findStringSwitchCase
swift-source
中查找_findStringSwitchCase
方法,接收两个参数,分别是 数组 + 需要匹配的String
总结:
-
enum中使用
rawValue
的本质是调用get
方法,即在get方法中从Mach-O对应地址中取出字符串并返回
的操作 -
enum中
init
方法的调用是通过枚举.init(rawValue:)
或者枚举(rawValue:)
触发的 -
如果希望获取所有枚举值,需要遵循
CaseIterable协议
,然后通过枚举名.allCase
的方式获取 -
case枚举值和rawValue原始值的关系:
case 枚举值 = rawValue原始值
未完待续....(下篇文章继续用sil方式分析枚举)