文章目录
  1. 1. 枚举
  2. 2. 可选项

枚举

打印枚举成员默认输出的是其本身。

1
2
3
4
5
6
7
8
enum Direction {
case north
case south
case east
case west
}
var dir = Direction.west
print(dir) //输出为: west

原始值:枚举成员可以预先设置相同类型的默认值,这个默认值就叫做原始值。使用rawValue获取其原始值。

1
2
3
4
5
6
7
8
9
10
enum Grade : Character {
case perfect = "A"
case great = "B"
case good = "C"
case bad = "D"
}
var good = Grade.good
print(good) // good
print(good.rawValue) // C
print(Grade.great.rawValue) // B

注意:原始值不占用枚举变量的内存。

如果枚举的原始值类型是IntString,Swift会自动分配原始值。Int默认的原始值从0递增,String默认的原始值是枚举成员的名字。

1
2
3
4
5
6
7
8
enum Direction : String {
case north
case south
case east
case west
}
print(Direction.south) // south
print(Direction.south.rawValue) // south

关联值:有时将枚举的成员值跟其他类型的值关联存储在一起。

1
2
3
4
5
6
7
8
9
10
11
12
13
enum Score {
case points(Int)
case grade(Character)
}

var score = Score.points(99)
score = .grade("A")
switch score {
case let .points(i):
print(i)
case let .grade(i):
print(i) // A
}

递归枚举:枚举成员的关联值类型是其自身。在使用递归枚举时,前方必须有indirect关键字修饰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
indirect enum ArithExpr {
case number(Int)
case sum(ArithExpr, ArithExpr)
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 diff = 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)
}
}
print(calculate(diff)) // 7

下面分析一下枚举的内存

1
2
3
4
enum Password {
case number(Int,Int,Int,Int)
case other
}

四个Int32个字节,然后需要注意的是case other,由于other没有内容,系统会分配给它一个字节的内存空间用来标记它, 所以实际需要的内存为33个字节,另外由于内存对齐参数是8字节,所以分配的占用空间为40

MemoryLayout可以获取数据类型占用的内存大小。

1
2
3
4
5
var pwd = Password.number(1, 4, 5, 9)
pwd = .other
MemoryLayout.stride(ofValue: pwd) // 40
MemoryLayout.size(ofValue: pwd) // 33
MemoryLayout.alignment(ofValue: pwd) // 8

需要注意的是Bool类型的内存对齐参数是1

还需要注意下面的情况:

1
2
3
4
5
6
7
8
enum TestEnum {
case test,test1,test2,test3
}
// 这里只需要一个字节就可以标记出来这四个枚举成员
var t = TestEnum.test
MemoryLayout.stride(ofValue: t) // 1
MemoryLayout.size(ofValue: t) // 1
MemoryLayout.alignment(ofValue: t) // 1
1
2
3
4
5
6
7
enum TestEnum {
case test
}
var t = TestEnum.test
MemoryLayout.stride(ofValue: t) // 1
MemoryLayout.size(ofValue: t) // 0
MemoryLayout.alignment(ofValue: t) // 1

MemoryLayout.size(ofValue: t) 为什么它的值是0呢???

因为该枚举类型只有一个成员,所以使用枚举变量的起始地址就可以表示(标记)这个仅有的枚举成员了,因此不需要单独分配内存去标记了,所以size为0。

再看下面这个例子:

1
2
3
4
5
6
7
enum TestEnum {
case test(Int)
}
var t = TestEnum.test(10)
MemoryLayout.stride(ofValue: t) // 8
MemoryLayout.size(ofValue: t) // 8
MemoryLayout.alignment(ofValue: t) // 8

实际只需要占用的空间是8,而不是8+1,因为只有一个枚举成员,不需要单独的一个字节去区分枚举成员,因此实际占用和分配的空间都是8。

原始值不占用内存空间,关联值占用枚举的内存。上面看了关联值的内存例子了,下面看看原始值的例子:

1
2
3
4
5
6
7
enum TT:Int {
case test=1,test1,test2,test3
}
let t = TT.test
MemoryLayout.stride(ofValue: t) // 1
MemoryLayout.size(ofValue: t) // 1
MemoryLayout.alignment(ofValue: t) // 1

因为原始值的具体值是枚举变量通过 rawValue 获取到的,所以并不需要在枚举内存中存储。

而关联值,每个枚举变量对应的关联值都是后来赋值的,不同枚举变量的关联值可能是不同的,所以需要存储在枚举变量的内存中。

可选项

一个主意的细节: nil 强制解包会报错。但是 Optional(Opational(nil))强制解包后为Optional(nil) ,因此不会报错。

空合并运算符??的运算规则:

a ?? b

a 是可选项

b是可选项或者不是可选项

b跟a的存储类型必须相同

如果a不为nil,就返回a

如果a为nil,就返回b

如果b不是可选项,返回a时会自动解包

多重可选项需要注意的地方:

1
2
3
4
var num1:Int? = 10
var num2:Int?? = num1
var num3:Int?? = 10
print(num2 == num3) // true
1
2
3
4
var num1:Int? = nil
var num2:Int?? = num1
var num3:Int?? = nil
print(num2 == num3) // false

第二个结果是 false 因为num3本身是空的nil,但是num2并不是空的,可以表示成Opational(nil)。

那么num1和num3是否相等呢?

当然是不相等的,因为他们本身的类型就不一样,一个是Int?,一个是Int?? 。

 



扫描下面二维码关注公众号,干货大集合:

文章目录
  1. 1. 枚举
  2. 2. 可选项