枚举 打印枚举成员默认输出的是其本身。
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
注意:原始值不占用枚举变量的内存。
如果枚举的原始值类型是Int
、String
,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 }
四个Int
占32
个字节,然后需要注意的是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?? 。
扫描下面二维码关注公众号,干货大集合: