枚举
枚举值是我们开发过程中最常见的类型之一,在OC中枚举值的实际类型是NSInterge。而在Swift中,枚举值赋予了更多,更灵活的用法,了解了枚举的用法,可以让我们代码更简洁,逻辑更清晰。 swfit中使用关键子
enum
来声明一个枚举类型。
enum OSCardType {
case OSCardTypeOne
case OSCardTypeTwo
case OSCardTypeThree
}
Swift 中的枚举非常灵活,并且不需给枚举中的每一个成员都提供值。如果一个值(所谓“原 始”值)要被提供给每一个枚举成员,那么这个值可以是字符串、字符、任意的整数值,或者是 浮点类型。
enum OSDeviceCode: String {
case OSDeviceCodeLight = "light"
case OSDeviceCodeAirConditioner = "airConditioner"
}
RawValue
也可以通过编译器去推断, 这是建立在 Swift 的类型推断机制上的
enum OSDayOfWeek: Int {
case mon, tue, wed, thu, fri = 10, sat, sun
}
mon = 0, ture = 1。。。。sat = 11, sun = 12。
如果是String类型,rawValue
默认就是枚举的字符串。
关联值
除了以上用法,Swift中枚举值还能携带更多信息,从而来表达更复杂的案例。可以跟随若干个关联值。
enum OSShape {
case circle(redious: Double)
case ractange(width: Double, height: Double)
}
在模式比配的时候要注意,Swift枚举值的case需要匹配所有枚举类型,否则编译器将会报错。带有关联值的模式匹配时候,关联值以参数的方式出现。
func func1() {
let cricle = OSShape.circle(redious: 10.0)
switch cricle {
case .circle(let circel):
print(circel)
case .ractange(let width, let height):
print(width, height)
}
}
枚举占内存的大小
枚举是值类型,是存储在栈上面的。存储类型分为两种,No-payload enum
和single-payload enum
。
可以通过打印来查看枚举的大小。
func func2() {
print(MemoryLayout<OSCardType>.size)
var card1 = OSCardType.OSCardTypeOne
var card2 = OSCardType.OSCardTypeTwo
print(card1,card2)
}
我们继续看看带关联值的内存大小。
enum SingleEnum1 {
case One(isgood: Bool)
case Two
case Three
}
enum SingleEnum2 {
case One(age: Int)
case Two
case Three
}
func func3() {
print(MemoryLayout<SingleEnum1>.size)
print(MemoryLayout<SingleEnum2>.size)
}
输出1、9
这里需要注意:SingleEnum1
是系统做了内存优化,节省了内存开支,Bool类型实际只用了1bite存储值,剩下的位用来存放枚举信息。网上有很多资料显示枚举的大小等于,最大的关联类型所占内存+枚举值内存,这里Bool类型是一个例外。非常容易忽略。在源码中也可以找到相应的描述。
大概翻译一下:如果储存空间够存放关联值,那么负载大小和枚举值大小一样。
indeirect关键字
分析:值类型在编译时期就已经确定了大小,而上面这种“套娃”方式,无法确定内存大小。我们使用编译器的修改方法。
indirect enum BinaryTree<T> {
case empty
case node(left: BinaryTree, right: BinaryTree, value: T)
}
func func4() {
var node = BinaryTree<Int>.node(left: BinaryTree<Int>.empty, right: BinaryTree<Int>.empty, value: 1)
print(node)
print(MemoryLayout<BinaryTree<Int>>.size)
print(MemoryLayout<BinaryTree<Int>>.stride)
}
输出位 8 8
,继续使用lldb打印地址看看。
这时候发现,当使用
indirect
关键字修饰后,系统会在堆区给该枚举分配空间。
可选值
可选值在我们swift编程的过程中随处可见,主要是为了安全性考虑设计出来的。可选值其实也是通过枚举来实现的。
class InFuncRoom {
var roomId: String?
}
也可以这样写:
class InFuncRoom {
// var roomId: String?
var roomId: Optional<String> = nil
}
点击Optional看实现
显然这个可选值就是一个枚举。
通过一个实例,来使用一下类似这种处理,从而体验可选值的使用。
题目:从数组中剔除所有偶数。
//自己实现可选值
func func6() {
var array = [1,2,3,4,5,6]
for value in array {
let optional = getOddValue(value)
switch optional {
case .some(let ele):
array.remove(at: array.firstIndex(of: ele)!)
print(ele)
default:
print("not exit")
}
}
print(array);
}
func getOddValue(_ value: Int) -> MyOptional<Int> {
if value % 2 == 0 {
return .some(value)
} else {
return .none
}
}
解包
如果我们每一个可选值都用模式匹配的方式(switch case
)来获取值,代码书写上回比较繁琐,我们可以使用 if let
的方式来进行可选值绑定(解包)。
func func7() {
var name: String? = "hahaha"
if let name = name {
print(name)
}
name = nil
if let name = name {
print(name)
}
}
除了使用 if let
来处理可选值之外,我们还可以使用 gurad let
来简化我们的代码,我们 来看一个具体的案例。
guard let
和 if let
刚好相反, guard let
守护一定有值。如果没有,直接返回。 通常判断是否有值之后,会做具体的逻辑实现,通常代码多 如果用 if let
凭空多了一层分支,guard let
是降低分支层次的办法
运算符重载
struct Vector {
let x: Int
let y: Int
}
extension Vector {
static func + (fistVector: Vector, secondVector: Vector) -> Vector {
return Vector(x: fistVector.x + secondVector.x, y: fistVector.y + secondVector.y)
}
static prefix func - (vector: Vector) -> Vector {
return Vector(x: -vector.x, y: -vector.y)
}
static func - (fistVector: Vector, secondVector: Vector) -> Vector {
return fistVector + -secondVector
}
}
func func6() {
let x = Vector(x: 10, y: 20)
let y = Vector(x: 80, y: 50)
let z = x + y
print(z)
}