enum Direction_1 {
case north, south, east, west
}
enum Direction {
case north
case south
case east
case west
}
var dir = Direction.west
dir = Direction.east
dir = .north
print(dir)
switch dir {
case .north:
print("north")
case .south:
print("south")
case .east:
print("east")
case .west:
print("west")
}
複製代碼
關聯值是直接存在枚舉變量的內存裏面的,這點要牢記
,對於一個有固定取值範圍的變量,設計成枚舉比較合適git
enum Score {
case points(Int)
case grade(Character)
}
var score = Score.points(96)
score = .grade("A")
switch score {
case let .points(i):
print(i, "points")
case let .grade(i):
print("grade", i)
} // grade A
enum Date {
case digit(year: Int, month: Int, day: Int)
case string(String)
}
var date = Date.digit(year: 2020, month: 02, day: 29)
date = .string("2020-02-29")
switch date {
case let .digit(year, month, day):
print(year, month, day)
case let .string(dateStr):
print(dateStr)
} // "2020-02-29"
複製代碼
注意上看switch內部對
let/var
關鍵字的使用,若是下載枚舉值左邊,那麼關聯值只能統一綁定給let常量
或者var變量
github
case let .digit(year, month, day): //year、month、day都是let常量
case var .digit(year, month, day): //year、month、day都是var變量
複製代碼
若是let/var關鍵字寫在關聯值括號內,就比較靈活spring
case .digit(let year, var month, let day)
複製代碼
另一些枚舉舉例swift
enum Password {
case number(Int, Int, Int, Int)
case gesture(String)
}
var pwd = Password.number(3, 5, 7, 9)
pwd = .gesture("3259")
switch pwd {
case let .number(n1 , n2 , n3 , n4 ): //數字密碼
print("number is", n1, n2, n3, n4)
case let .gesture(pwdStr):// 字符串密碼
print("gestrue is", pwdStr)
}
複製代碼
枚舉成員能夠只用相同類型的默認值預先關聯,這個默認值叫作 原始值markdown
enum PokerSuit: Character { //這裏的Character表示的是枚舉值所關聯的原始值
case spade = "️"
case heart = "️"
case diamond = "️"
case club = "️"
}
var suit = PokerSuit.spade
print(suit)
print(suit.rawValue)
print(PokerSuit.club.rawValue)
enum Grade: String {
case perfect = "A"
case great = "B"
case good = "C"
case bad = "D"
}
print(Grade.perfect.rawValue) // A
print(Grade.great.rawValue) // B
print(Grade.good.rawValue) // C
print(Grade.bad.rawValue) // D
複製代碼
enum Direction1: String {
case north, south, east, west
}
print(Direction1.north.rawValue)
enum Direction2: String {
case north = "nor", south, east, west
}
print(Direction2.north.rawValue)//有賦值,就用賦值的字符串
print(Direction2.south.rawValue)//沒賦值, 就用case名字符串
enum Season: Int {
case spring, summer, autumn, winter
}
print(Season.spring.rawValue)//0
print(Season.summer.rawValue)//1
print(Season.autumn.rawValue)//2
print(Season.winter.rawValue)//3
enum Season2: Int {
case spring = 2, summer, autumn = 6, winter
}
print(Season2.spring.rawValue) //2
print(Season2.summer.rawValue) //3
print(Season2.autumn.rawValue) //6
print(Season2.winter.rawValue) //7
複製代碼
//書寫方法一
indirect enum ArithExpr_1 {
case number(Int)
case sum(ArithExpr, ArithExpr)
case difference(ArithExpr, ArithExpr)
}
//書寫方法二
enum ArithExpr {
case number(Int)
indirect case sum(ArithExpr, ArithExpr)
indirect 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 difference = 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)
}
}
複製代碼
咱們可使用
MemoryLayout
來獲取數據類型佔用的內存大小,至關於C裏面使用的sizeof
ide
enum Password2 {
case number(Int, Int, Int, Int)
case other
}
MemoryLayout<Password2>.stride //系統分配給變量的內存大小--40
MemoryLayout<Password2>.size //實際被使用的內存大小--33
MemoryLayout<Password2>.alignment //對其參數--8
var pd = Password2.number(9, 8, 7, 6)
pd = .other
print(pd) //"other/n"
MemoryLayout.stride(ofValue: pd) //40
MemoryLayout.size(ofValue: pd) //33
MemoryLayout.alignment(ofValue: pd) //8
複製代碼
經過MemoryLayout,咱們只能簡單查看一些內存相關的信息,但還不足以看清枚舉在內存中的具體細節,因爲Xcode調試工具沒法爲咱們提供枚舉變量的內存地址,所以須要藉助一些額外的工具,這裏推介一下大牛李明傑的一個工具。工具
(1)首先來看下一種簡單的狀況~~~~~~~oop
enum TestEnum {
case test1, test2, test3
}
print("系統實際分配內存",MemoryLayout<TestEnum>.stride)
print("實際使用的內存",MemoryLayout<TestEnum>.size)
print("內存對齊參數",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test1
print("枚舉變量t的內存地址:",Mems.ptr(ofVal: &t)) //這裏能夠輸出變量t的內存地址
t = .test2
t = .test3
print("Stop for debug")
複製代碼
Mems.ptr(ofVal: &t)
能夠幫咱們得到變量t的內存地址,準備好3個斷點 而後將程序運行值斷點1
處,此時咱們已經得到t
的內存地址,根據該地址,調出內存界面,咱們來觀察一下此時的內存細節 在繼續走到斷點二、斷點3處,對比一下各自的內存狀況以下 ui
小結:enum TestEnum { case test1, test2, test3 }
spa
- 系統爲TestEnum類型的變量分配
1個字節
的內存空間- test1 、 test二、 test3 三個case對應在內存中用整數0、一、2來表示
(2)把場景調整爲有Int
型原始值的情形以下~~~~~~~
enum TestEnum: Int {
case test1
case test2 = 3
case test3
case test4 = 10
case test5
}
print("系統實際分配內存",MemoryLayout<TestEnum>.stride)
print("實際使用的內存",MemoryLayout<TestEnum>.size)
print("內存對齊參數",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test1
print("枚舉變量t的內存地址:",Mems.ptr(ofVal: &t)) //這裏能夠輸出變量t的內存地址
t = .test2
t = .test3
t = .test4
t = .test5
print("Stop for debug")
複製代碼
按照上面一樣的方法,對比各自case
的內存狀況以下 咱們在查看一下各自case
的rawValue
print("test1的rawValue:", TestEnum.test1.rawValue)
print("test2的rawValue:", TestEnum.test2.rawValue)
print("test2的rawValue:", TestEnum.test3.rawValue)
print("test2的rawValue:", TestEnum.test4.rawValue)
print("test2的rawValue:", TestEnum.test5.rawValue)
***********運行結果
test1的rawValue: 0
test2的rawValue: 3
test2的rawValue: 4
test2的rawValue: 10
test2的rawValue: 11
複製代碼
看得出,若是原始值類型爲Int
:
case
的原始值默爲整數0
,非首個case
的默認值爲上一個case
的默認值+1
(3)看過了帶Int
型原始值的狀況以後,在看一下帶String
型原始值的狀況,改造以下~~~~~~~
enum TestEnum: String {
case test1
case test2 = "AA"
case test3 = "漢字"
case test4 = "🦕"
case test5
}
print("系統實際分配內存",MemoryLayout<TestEnum>.stride)
print("實際使用的內存",MemoryLayout<TestEnum>.size)
print("內存對齊參數",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test1
print("枚舉變量t的內存地址:",Mems.ptr(ofVal: &t)) //這裏能夠輸出變量t的內存地址
t = .test2
t = .test3
t = .test4
t = .test5
print("Stop for debug")
print("test1的rawValue:", TestEnum.test1.rawValue)
print("test2的rawValue:", TestEnum.test2.rawValue)
print("test2的rawValue:", TestEnum.test3.rawValue)
print("test2的rawValue:", TestEnum.test4.rawValue)
print("test2的rawValue:", TestEnum.test5.rawValue)
****************運行結果
系統實際分配內存 1
實際使用的內存 1
內存對齊參數 1
枚舉變量t的內存地址: 0x0000000100008218
Stop for debug
test1的rawValue: test1
test2的rawValue: AA
test2的rawValue: 漢字
test2的rawValue: 🦕
test2的rawValue: test5
Program ended with exit code: 0
複製代碼
內存的狀況這裏省略,和上面Int
型的時候是同樣的,根據調試輸出的狀況,咱們能夠看出
case
的原始值爲該case名稱
的字符串case
的原始值即爲設定值
總結 帶原始值的枚舉
- 枚舉變量自己的就佔一個字節
- 枚舉變量所對應的內存裏所存放的具體值:對應第一個case爲0,而且日後逐個+1
(4)帶關聯值的場景~~~~~~~
enum TestEnum {
case test1(a: Int, b: Int, c: Int)
case test2(d: Int, e: Int)
case test3(f: Int)
case test4(g: Bool)
case test5
}
print("系統實際分配內存",MemoryLayout<TestEnum>.stride)
print("實際使用的內存",MemoryLayout<TestEnum>.size)
print("內存對齊參數",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test1(a: 1, b: 2, c: 3)
//這裏能夠輸出變量t的內存地址
print("枚舉變量t的內存地址:",Mems.ptr(ofVal: &t))
t = .test2(d: 4, e: 5)
t = .test3(f: 6)
t = .test4(g: true)
t = .test5
print("Stop for debug")
*****************運行結果
系統實際分配內存 32
實際使用的內存 25
內存對齊參數 8
枚舉變量t的內存地址: 0x0000000100008208
複製代碼
接下來照例在過一遍內存,下面直接貼上內存查看的結果
t = test1(a: 1, b: 2, c: 3)
01 00 00 00 00 00 00 00 --> 對應a
02 00 00 00 00 00 00 00 --> 對應b
03 00 00 00 00 00 00 00 --> 對應c
00 00 00 00 00 00 00 00 --> 對應case test1
複製代碼
t = test2(d: 4, e: 5)
04 00 00 00 00 00 00 00 --> 對應d
05 00 00 00 00 00 00 00 --> 對應e
00 00 00 00 00 00 00 00 --> 此時沒用到
01 00 00 00 00 00 00 00 --> 對應case test2
複製代碼
t = test3(f: 6)
06 00 00 00 00 00 00 00 --> 對應f
00 00 00 00 00 00 00 00 --> 此時沒用到
00 00 00 00 00 00 00 00 --> 此時沒用到
02 00 00 00 00 00 00 00 --> 對應case test3
複製代碼
t = test4(g: true)
01 00 00 00 00 00 00 00 --> 對應g
00 00 00 00 00 00 00 00 --> 此時沒用到
00 00 00 00 00 00 00 00 --> 此時沒用到
03 00 00 00 00 00 00 00 --> 對應case test4
複製代碼
t = test5
00 00 00 00 00 00 00 00 --> 此時沒用到
00 00 00 00 00 00 00 00 --> 此時沒用到
00 00 00 00 00 00 00 00 --> 此時沒用到
04 00 00 00 00 00 00 00 --> 對應case test5
複製代碼
總結 帶關聯值的枚舉
- 枚舉變量的
成員case
的值只用了其內存空間的1字節來存放- 枚舉的
case關聯值
也存放在枚舉變量的內存中- 系統爲枚舉的
case關聯值
所分配的內存空間,必須保證能夠放下所需內存最大的那個關聯值- 枚舉變量的內存空間裏,先存放存放的是
case關聯值
,成員case
的值被放在最後- 枚舉變量的內存總空間按內存對齊參數進行補齊(計算機常識)
(5)一些極端場景~~~~~~~
enum TestEnum {
case test
}
print("系統實際分配內存",MemoryLayout<TestEnum>.stride)
print("實際使用的內存",MemoryLayout<TestEnum>.size)
print("內存對齊參數",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test
print(print("枚舉變量t的內存地址:",Mems.ptr(ofVal: &t)))
****************運行結果
系統實際分配內存 1
實際使用的內存 0
內存對齊參數 1
枚舉變量t的內存地址: 0x0000000000000001
Program ended with exit code: 0
複製代碼
能夠看到,系統確實是分配了1
個字節給枚舉,可是實際上用到了0
個,由於一種狀況不須要作任何區分,因此也就不須要存儲,固然貌似沒人會這麼用,因此係統針對這種狀況下的處理,就不難理解了。在看看帶關聯值的狀況:
enum TestEnum {
case test(Int)
}
print("系統實際分配內存",MemoryLayout<TestEnum>.stride)
print("實際使用的內存",MemoryLayout<TestEnum>.size)
print("內存對齊參數",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test(10)
print("枚舉變量t的內存地址:",Mems.ptr(ofVal: &t))
print("Stop for debug")
***************運行結果
系統實際分配內存 8
實際使用的內存 8
內存對齊參數 8
枚舉變量t的內存地址: 0x0000000100007200
Stop for debug
Program ended with exit code: 0
***************彙編結果
0A 00 00 00 00 00 00 00
複製代碼
能夠看到系統直接分配了8
個字節來存儲枚舉裏面的Int
型關聯值,沒有分配空間來存儲成員case
的值,緣由和上面很想,由於如今就是一種case
,沒有必要再存儲成員變量的值,只須要關心case關聯值
就好。那若是有一個以上的case,是否是就會給成員case
分配空間了?我們試試看,以下
enum TestEnum {
case other
case test(Int)
}
print("系統實際分配內存",MemoryLayout<TestEnum>.stride)
print("實際使用的內存",MemoryLayout<TestEnum>.size)
print("內存對齊參數",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.other
//Mem.memStr是大神李明傑提供的工具,文中有連接,能夠幫我直接獲取變量的內存裏面的值
print("枚舉變量t = other 時的內存狀況: ",Mems.memStr(ofVal: &t))
t = TestEnum.test(10)
print("枚舉變量t = test(10)時的內存地址:",Mems.memStr(ofVal: &t))
print("Stop for debug")
***************運行結果
系統實際分配內存 16
實際使用的內存 9
內存對齊參數 8
枚舉變量t = other 時的內存狀況: 0x0000000000000000 0x0000000000000001
枚舉變量t = test(10)時的內存地址: 0x000000000000000a 0x0000000000000000
複製代碼
能夠看出,只要case
大於1
個,除了Int
型關聯值須要佔用8
個字節外,枚舉變量還使用了1
個字節來存儲成員case
的值,根據內存對齊參數8
,系統給枚舉變量分配了16
字節空間。上面的結果中,最後一個字節是用來存放成員case
的值也就是case other
對應了01
, case test
對應了00
,可是感受順序不太對,明明是other
在前,test
在後的,帶着這個疑問,咱們把用例改造以下
enum TestEnum {
case aaa
case test(Int)
case ccc
case test3(Int, Int)
case test2(Int,Int, Int)
case other
}
print("系統實際分配內存",MemoryLayout<TestEnum>.stride)
print("實際使用的內存",MemoryLayout<TestEnum>.size)
print("內存對齊參數",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.aaa
print("t = .aaa的內存狀況: ",Mems.memStr(ofVal: &t))
t = TestEnum.test(10)
print("t = .test(10)的內存狀況: ",Mems.memStr(ofVal: &t))
t = TestEnum.ccc
print("t = .ccc的內存狀況: ",Mems.memStr(ofVal: &t))
t = TestEnum.test3(16, 32)
print("t = .test3(16, 32)的內存狀況: ",Mems.memStr(ofVal: &t))
t = TestEnum.test2(20, 20, 20)
print("t = .test2(20, 20, 20)的內存狀況:",Mems.memStr(ofVal: &t))
t = TestEnum.other
print("t = .other的內存狀況: ",Mems.memStr(ofVal: &t))
print("Stop for debug")
*************************************運行結果
系統實際分配內存 32
實際使用的內存 25
內存對齊參數 8
t = .aaa的內存狀況: 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000003
t = .test(10)的內存狀況: 0x000000000000000a 0x0000000000000000 0x0000000000000000 0x0000000000000000
t = .ccc的內存狀況: 0x0000000000000001 0x0000000000000000 0x0000000000000000 0x0000000000000003
t = .test3(16, 32)的內存狀況: 0x0000000000000010 0x0000000000000020 0x0000000000000000 0x0000000000000001
t = .test2(20, 20, 20)的內存狀況: 0x0000000000000014 0x0000000000000014 0x0000000000000014 0x0000000000000002
t = .other的內存狀況: 0x0000000000000002 0x0000000000000000 0x0000000000000000 0x0000000000000003
Stop for debug
Program ended with exit code: 0
複製代碼
從上面的調試,又挖掘了一點小細節:
成員case
,它的case
值會根據定義的順序,默認從0開始+1累加,成員case
,它們的case
值相同,並且都等於最後一個可關聯成員case 的值+1
以上咱們看清楚了簡單枚舉、關聯值枚舉、原始值枚舉在內存中分別是如何存儲的,能夠看出,枚舉的關聯值和原始值又如下區別:
xx.rawValue
訪問的,所以它的值徹底不須要存儲,能夠在枚舉定義完以後經過方法提供給外部。enum Direction
: String/Int/...
{...}
。關聯值則必須在枚舉定義的時候,肯定好case
所對應的關聯值類型case
被賦值給變量的時候進行賦值,由於同一個case
每次被賦值給變量,都須要設定一個關聯值,所以也能夠說關聯值是能夠改變的,以下enum Score {
case points(Int)
case grade(Character)
}
var score = Score.points(96)
score = .grade("A")
score = .grade("B") -->相同的case,不一樣的關聯值
複製代碼
而原始值,只能在枚舉定義的時候進行賦值,不賦值則系統會給定相應的默認值,也就是隻有一次機會能夠賦值,定義完枚舉以後,就沒有辦法能夠更改原始值了,示例以下
enum Grade: String {
case perfect = "A"
case great
case good = "C"
case bad = "D"
}
print(Grade.perfect.rawValue) --> A
print(Grade.great.rawValue) --> 定義時無賦值,系統默認爲case的名稱 great
print(Grade.good.rawValue) --> C
print(Grade.bad.rawValue) -> D
複製代碼
switch
的實現原理(待續...)