1、Enum
1.1、基本用法
swift中通过enum
关键字来声明一个枚举。
enum YYEnum {
case YYEnum_one
case YYEnum_two
case YYEnum_three
}
1.2、原始值(rawValue)
跟oc不同(只能是NSUInteger类型),swift的枚举值有更多类型选择。swift的枚举不需要给枚举中的每一个成员都提供值。如果一个值(原始值)需要被提供给每一个枚举成员,这个原始值可以是字符串、字符、任意的整形或者是浮点类型。
enum Color : String {
case red = "Red"
case amber = "Amber"
case green = "Green"
}
enum DoubleEnum: Double {
case a = 10.0
case b = 20.0
case c = 30.0
case d = 40.0
}
enum DayOfWeekInt :Int{
case mon, tue, wed, thu, fri = 10, sat, sun
}
enum DayOfWeekString :String{
case mon, tue, wed, thu, fri = "fri", sat, sun
}
当Int类型的枚举设置了某一个枚举的原始值(rawValue
),那么这个枚举值后面的原始值就会根据设置的原始值进行累加
String
枚举,默认的原始值就是case的本身,如果有单独设置,就是设置的值。
sil代码分析
enum DayOfWeekString :String{
case mon, tue, wed, thu, fri = "hello fri", sat, sun
}
var day = DayOfWeekString.fri.rawValue
将代码转成sil
枚举类型获取原始值的本质就是通过rawValue.getter
方法,getter方法中通过模式匹配,返回不同的枚举原始值。
1.3、关联值
关联值可以通过()的形式,放在枚举值的后面。可以表达更复杂的数据。
//表示形状的枚举类型
enum Shape{
//表示圆形,需要一个半径参数
case circle(_ radious: Double)
//表示矩形形,需要宽和高
case rectangle( _ width: Int,_ height: Int)
}
var circle = Shape.circle(10)
var rectangle = Shape.rectangle(20, 10)
可以明确看到的是,当设置枚举的关联值之后,就不存在原始值了。
1.4、模式匹配
enum DayOfWeekString :String{
case mon, tue, wed, thu, fri = "hello fri", sat, sun
}
var day = DayOfWeekString.fri
switch day {
case .mon:
print(DayOfWeekString.mon.rawValue)
case .tue:
print(DayOfWeekString.tue.rawValue)
case .wed:
print(DayOfWeekString.wed.rawValue)
case .thu:
print(DayOfWeekString.thu.rawValue)
case .fri:
print(DayOfWeekString.fri.rawValue)
//case .sat:
// print(DayOfWeekString.sat.rawValue)
//case .sun:
// print(DayOfWeekString.sun.rawValue)
default:print("happy day")
}
//输出结果:hello fri
可以匹配所有的case值,也可以用default
关键字
关联值的匹配
//表示形状的枚举类型
enum Shape{
//表示圆形,需要一个半径参数
case circle(_ radious: Double)
//表示矩形形,需要宽和高
case rectangle( _ width: Int,_ height: Int)
}
var circle = Shape.circle(10)
var rectangle = Shape.rectangle(20, 10)
switch circle {
case .circle(let _radious):
print("circle radious: \(_radious)")
case .rectangle(let _width, let _height):
print("rectangle width: \(_width), height: \(_height)")
}
//输出结果circle radious: 10.0
1.5、枚举的大小
1.5.1、没有关联值的枚举大小(No-payload enums
)
枚举的case值默认是UInt8
类型,只占一个字节。
当前的enum
有7个case
,在swift进行枚举空间布局的时候一直是尝试用最少的空间来存储,按照UInt8
类型存储,可以存储256个case
。所以没有关联值类型的枚举,只要case不超过256个,大小是1。
1.5.2、有一个关联值的枚举大小(single-payload enums
)
关联值是Bool
类型
当有一个Bool
类型的关联值时,大小还是1字节。因为Bool
类型是1字节,只需要占8位中的最后一位,还有7位剩余空间,可以用来存储case
值,此时的case
最多只能有127个。
关联值是Int
类型
当有一个Int
类型的关联值时,大小是9字节。因为不知道Int
类型具体存的值,没法判断具体大小,只能给8字节。发现没有剩余空间可以用来存储case
值,所以要单独给1字节存储,最终实际大小9字节。
1.5.3、有多个关联值的枚举大小(Mutil-payload enums
)
可以看到大小还是1,因为Bool
类型剩余的位数能存储下所有的case
。
大小就变成了sizeof(Int)
* 2 + sizeof(rawVlaue)
= 17
1.5.4、总结
- 当枚举的关联值,剩余的空间能够存放下
case
值,那么枚举的大小就是关联值的大小 - 当枚举的关联值,剩余的空间不能够存放下
case
值,那么枚举的大小就是关联值的大小 +case
值大小
2、Optional
2.1、常用写法
class YYPeople {
var age :Int?
var height :Optional<Double>
init(_ age: Int, _ height: Double) {
self.age = age
self.height = height
}
}
var p = YYPeople.init(18, 185.0)
print(p.age!)
print(p.height!)
//控制台输出
//18
//185.0
YYPeople
中age
和height
就是可选类型。
?
和Optional<T>
的写法是等价的,?
就是Optional
的语法糖。
2.2、Optional的本质
源码中找到Optional.swift
可以看到Optional
的本质就是enum
。
2.3、模式匹配
使用Optional
实现输出数组中的偶数值
//奇数返回值,否则返回nil
func getOddValue (_ value: Int) -> Int? {
if value % 2 == 1{
return .some(value)
}else{
return .none
}
}
var array = [1,2,3,4,5,6,7,8,9]
for element in array {
let value = getOddValue(element)
//模式匹配
switch value {
case.some(let value):
array.remove(at: array.firstIndex(of: value)!)
case.none :
print("vlaue not exist")
}
}
print(array)
如果每一个可选值都用模式匹配的方式来获取值在代码书写上就比较繁琐,我们还可以使 用 if let 的方式来进行可选值绑定
除了使用 if let
来处理可选值之外,我们还可以使用 gurad let
来简化我们的代码,我们来看一个具体的案例
gurad let
和 if let
刚好相反,守护一定有值。如果没有,直接返回。 通常判断是否有值之后,会做具体的逻辑实现。如果用 if let
凭空多了一层分支,gurad let
是降低分支层次的办法。
2.4、可选链
在 OC
中我们给一个 nil
对象发送消息什么也不会发生, Swift
中我们是没有办 法向一个nil
对象直接发送消息,但是借助可选链可以达到类似的效果。
可选链对于函数调用和下标也适用
2.5、?? 运算符 (空合并运算符)
( a ?? b ) 将对可选类型 a 进行空判断,如果 a 包含一个值就进行解包,否则就返回 一个默认值 b 。
- 表达式 a 必须是 Optional 类型
- 默认值 b 的类型必须要和 a 存储值的类型保持一致
2.6、运算符重载
通过重载运算符,可以简化我们的表达式。
在Optional
源码中,重载了??
运算符。