枚舉的基本你用法
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")
}
關(guān)聯(lián)值(Associated Values)
關(guān)聯(lián)值是直接存在枚舉變量的內(nèi)存里面的疮绷,這點(diǎn)要牢記
,對(duì)于一個(gè)有固定取值范圍的變量棒动,設(shè)計(jì)成枚舉比較合適
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內(nèi)部對(duì)
let/var
關(guān)鍵字的使用贿条,如果下載枚舉值左邊今魔,那么關(guān)聯(lián)值只能統(tǒng)一綁定給let常量
或者var變量
case let .digit(year, month, day): //year、month顷级、day都是let常量
case var .digit(year, month, day): //year凫乖、month、day都是var變量
如果let/var關(guān)鍵字寫在關(guān)聯(lián)值括號(hào)內(nèi)弓颈,就比較靈活
case .digit(let year, var month, let day)
另外一些枚舉舉例
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 ): //數(shù)字密碼
print("number is", n1, n2, n3, n4)
case let .gesture(pwdStr):// 字符串密碼
print("gestrue is", pwdStr)
}
原始值(Raw Values)
枚舉成員可以只用相同類型的默認(rèn)值預(yù)先關(guān)聯(lián)帽芽,這個(gè)默認(rèn)值叫做 原始值
enum PokerSuit: Character { //這里的Character表示的是枚舉值所關(guān)聯(lián)的原始值
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
隱式原始值(Implicitly Assigned Raw Values)
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
遞歸枚舉(Recursive Enumeration)
//書寫方法一
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
我們可以使用
MemoryLayout
來獲取數(shù)據(jù)類型占用的內(nèi)存大小导街,相當(dāng)于C里面使用的sizeof
enum Password2 {
case number(Int, Int, Int, Int)
case other
}
MemoryLayout<Password2>.stride //系統(tǒng)分配給變量的內(nèi)存大小--40
MemoryLayout<Password2>.size //實(shí)際被使用的內(nèi)存大小--33
MemoryLayout<Password2>.alignment //對(duì)其參數(shù)--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
枚舉在內(nèi)存中是如何存儲(chǔ)的?
通過MemoryLayout纤子,我們只能簡(jiǎn)單查看一些內(nèi)存相關(guān)的信息搬瑰,但還不足以看清枚舉在內(nèi)存中的具體細(xì)節(jié),由于Xcode調(diào)試工具無法為我們提供枚舉變量的內(nèi)存地址控硼,因此需要借助一些額外的工具泽论,這里推介一下大牛李明杰的一個(gè)工具。
(1)首先來看下一種簡(jiǎn)單的情況~~~~~~~
enum TestEnum {
case test1, test2, test3
}
print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test1
print("枚舉變量t的內(nèi)存地址:",Mems.ptr(ofVal: &t)) //這里可以輸出變量t的內(nèi)存地址
t = .test2
t = .test3
print("Stop for debug")
Mems.ptr(ofVal: &t)
可以幫我們獲得變量t的內(nèi)存地址卡乾,準(zhǔn)備好3個(gè)斷點(diǎn)
然后將程序運(yùn)行值
斷點(diǎn)1
處翼悴,此時(shí)我們已經(jīng)獲得t
的內(nèi)存地址,根據(jù)該地址幔妨,調(diào)出內(nèi)存界面鹦赎,我們來觀察一下此時(shí)的內(nèi)存細(xì)節(jié)在繼續(xù)走到斷點(diǎn)2、斷點(diǎn)3處陶冷,對(duì)比一下各自的內(nèi)存情況如下
小結(jié):enum TestEnum { case test1, test2, test3 }
- 系統(tǒng)為TestEnum類型的變量分配
1個(gè)字節(jié)
的內(nèi)存空間- test1 钙姊、 test2、 test3 三個(gè)case對(duì)應(yīng)在內(nèi)存中用整數(shù)0埂伦、1煞额、2來表示
(2)把場(chǎng)景調(diào)整為有Int
型原始值的情形如下~~~~~~~
enum TestEnum: Int {
case test1
case test2 = 3
case test3
case test4 = 10
case test5
}
print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test1
print("枚舉變量t的內(nèi)存地址:",Mems.ptr(ofVal: &t)) //這里可以輸出變量t的內(nèi)存地址
t = .test2
t = .test3
t = .test4
t = .test5
print("Stop for debug")
按照上面同樣的方法,對(duì)比各自case
的內(nèi)存情況如下
我們?cè)诓榭匆幌赂髯?code>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)
***********運(yùn)行結(jié)果
test1的rawValue: 0
test2的rawValue: 3
test2的rawValue: 4
test2的rawValue: 10
test2的rawValue: 11
看得出沾谜,如果原始值類型為Int
:
- 那么在不手動(dòng)設(shè)定的情況下膊毁,首個(gè)
case
的原始值默為整數(shù)0
,非首個(gè)case
的默認(rèn)值為上一個(gè)case
的默認(rèn)值+1
- 如果手動(dòng)設(shè)定了基跑,那么原始值即為設(shè)定值婚温。
(3)看過了帶Int
型原始值的情況之后,在看一下帶String
型原始值的情況媳否,改造如下~~~~~~~
enum TestEnum: String {
case test1
case test2 = "AA"
case test3 = "漢字"
case test4 = "??"
case test5
}
print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test1
print("枚舉變量t的內(nèi)存地址:",Mems.ptr(ofVal: &t)) //這里可以輸出變量t的內(nèi)存地址
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)
****************運(yùn)行結(jié)果
系統(tǒng)實(shí)際分配內(nèi)存 1
實(shí)際使用的內(nèi)存 1
內(nèi)存對(duì)齊參數(shù) 1
枚舉變量t的內(nèi)存地址: 0x0000000100008218
Stop for debug
test1的rawValue: test1
test2的rawValue: AA
test2的rawValue: 漢字
test2的rawValue: ??
test2的rawValue: test5
Program ended with exit code: 0
內(nèi)存的情況這里省略栅螟,和上面Int
型的時(shí)候是一樣的荆秦,根據(jù)調(diào)試輸出的情況,我們可以看出
- 如果不設(shè)置原始值力图,那么
case
的原始值為該case名稱
的字符串 - 如果設(shè)置了原始值步绸,那嗎
case
的原始值即為設(shè)定值
總結(jié) 帶原始值的枚舉
- 枚舉變量本身的就占一個(gè)字節(jié)
- 枚舉變量所對(duì)應(yīng)的內(nèi)存里所存放的具體值:對(duì)應(yīng)第一個(gè)case為0,并且往后逐個(gè)+1
(4)帶關(guān)聯(lián)值的場(chǎng)景~~~~~~~
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("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test1(a: 1, b: 2, c: 3)
//這里可以輸出變量t的內(nèi)存地址
print("枚舉變量t的內(nèi)存地址:",Mems.ptr(ofVal: &t))
t = .test2(d: 4, e: 5)
t = .test3(f: 6)
t = .test4(g: true)
t = .test5
print("Stop for debug")
*****************運(yùn)行結(jié)果
系統(tǒng)實(shí)際分配內(nèi)存 32
實(shí)際使用的內(nèi)存 25
內(nèi)存對(duì)齊參數(shù) 8
枚舉變量t的內(nèi)存地址: 0x0000000100008208
接下來照例在過一遍內(nèi)存吃媒,下面直接貼上內(nèi)存查看的結(jié)果
t = test1(a: 1, b: 2, c: 3)
01 00 00 00 00 00 00 00 --> 對(duì)應(yīng)a
02 00 00 00 00 00 00 00 --> 對(duì)應(yīng)b
03 00 00 00 00 00 00 00 --> 對(duì)應(yīng)c
00 00 00 00 00 00 00 00 --> 對(duì)應(yīng)case test1
t = test2(d: 4, e: 5)
04 00 00 00 00 00 00 00 --> 對(duì)應(yīng)d
05 00 00 00 00 00 00 00 --> 對(duì)應(yīng)e
00 00 00 00 00 00 00 00 --> 此時(shí)沒用到
01 00 00 00 00 00 00 00 --> 對(duì)應(yīng)case test2
t = test3(f: 6)
06 00 00 00 00 00 00 00 --> 對(duì)應(yīng)f
00 00 00 00 00 00 00 00 --> 此時(shí)沒用到
00 00 00 00 00 00 00 00 --> 此時(shí)沒用到
02 00 00 00 00 00 00 00 --> 對(duì)應(yīng)case test3
t = test4(g: true)
01 00 00 00 00 00 00 00 --> 對(duì)應(yīng)g
00 00 00 00 00 00 00 00 --> 此時(shí)沒用到
00 00 00 00 00 00 00 00 --> 此時(shí)沒用到
03 00 00 00 00 00 00 00 --> 對(duì)應(yīng)case test4
t = test5
00 00 00 00 00 00 00 00 --> 此時(shí)沒用到
00 00 00 00 00 00 00 00 --> 此時(shí)沒用到
00 00 00 00 00 00 00 00 --> 此時(shí)沒用到
04 00 00 00 00 00 00 00 --> 對(duì)應(yīng)case test5
總結(jié) 帶關(guān)聯(lián)值的枚舉
- 枚舉變量的
成員case
的值只用了其內(nèi)存空間的1字節(jié)來存放- 枚舉的
case關(guān)聯(lián)值
也存放在枚舉變量的內(nèi)存中- 系統(tǒng)為枚舉的
case關(guān)聯(lián)值
所分配的內(nèi)存空間瓤介,必須保證可以放下所需內(nèi)存最大的那個(gè)關(guān)聯(lián)值- 枚舉變量的內(nèi)存空間里,先存放存放的是
case關(guān)聯(lián)值
赘那,成員case
的值被放在最后- 枚舉變量的內(nèi)存總空間按內(nèi)存對(duì)齊參數(shù)進(jìn)行補(bǔ)齊(計(jì)算機(jī)常識(shí))
(5)一些極端場(chǎng)景~~~~~~~
enum TestEnum {
case test
}
print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test
print(print("枚舉變量t的內(nèi)存地址:",Mems.ptr(ofVal: &t)))
****************運(yùn)行結(jié)果
系統(tǒng)實(shí)際分配內(nèi)存 1
實(shí)際使用的內(nèi)存 0
內(nèi)存對(duì)齊參數(shù) 1
枚舉變量t的內(nèi)存地址: 0x0000000000000001
Program ended with exit code: 0
可以看到刑桑,系統(tǒng)確實(shí)是分配了1
個(gè)字節(jié)給枚舉,但是實(shí)際上用到了0
個(gè)募舟,因?yàn)橐环N情況不需要做任何區(qū)分祠斧,所以也就不需要存儲(chǔ),當(dāng)然貌似沒人會(huì)這么用拱礁,所以系統(tǒng)針對(duì)這種情況下的處理梁肿,就不難理解了。在看看帶關(guān)聯(lián)值的情況:
enum TestEnum {
case test(Int)
}
print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test(10)
print("枚舉變量t的內(nèi)存地址:",Mems.ptr(ofVal: &t))
print("Stop for debug")
***************運(yùn)行結(jié)果
系統(tǒng)實(shí)際分配內(nèi)存 8
實(shí)際使用的內(nèi)存 8
內(nèi)存對(duì)齊參數(shù) 8
枚舉變量t的內(nèi)存地址: 0x0000000100007200
Stop for debug
Program ended with exit code: 0
***************匯編結(jié)果
0A 00 00 00 00 00 00 00
可以看到系統(tǒng)直接分配了8
個(gè)字節(jié)來存儲(chǔ)枚舉里面的Int
型關(guān)聯(lián)值觅彰,沒有分配空間來存儲(chǔ)成員case
的值吩蔑,原因和上面很想,因?yàn)楝F(xiàn)在就是一種case
填抬,沒有必要再存儲(chǔ)成員變量的值烛芬,只需要關(guān)心case關(guān)聯(lián)值
就好。那如果有一個(gè)以上的case飒责,是不是就會(huì)給成員case
分配空間了赘娄?咱們?cè)囋嚳矗缦?/p>
enum TestEnum {
case other
case test(Int)
}
print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.other
//Mem.memStr是大神李明杰提供的工具宏蛉,文中有鏈接遣臼,可以幫我直接獲取變量的內(nèi)存里面的值
print("枚舉變量t = other 時(shí)的內(nèi)存情況: ",Mems.memStr(ofVal: &t))
t = TestEnum.test(10)
print("枚舉變量t = test(10)時(shí)的內(nèi)存地址:",Mems.memStr(ofVal: &t))
print("Stop for debug")
***************運(yùn)行結(jié)果
系統(tǒng)實(shí)際分配內(nèi)存 16
實(shí)際使用的內(nèi)存 9
內(nèi)存對(duì)齊參數(shù) 8
枚舉變量t = other 時(shí)的內(nèi)存情況: 0x0000000000000000 0x0000000000000001
枚舉變量t = test(10)時(shí)的內(nèi)存地址: 0x000000000000000a 0x0000000000000000
可以看出,只要case
大于1
個(gè)拾并,除了Int
型關(guān)聯(lián)值需要占用8
個(gè)字節(jié)外揍堰,枚舉變量還使用了1
個(gè)字節(jié)來存儲(chǔ)成員case
的值,根據(jù)內(nèi)存對(duì)齊參數(shù)8
嗅义,系統(tǒng)給枚舉變量分配了16
字節(jié)空間屏歹。上面的結(jié)果中,最后一個(gè)字節(jié)是用來存放成員case
的值也就是case other
對(duì)應(yīng)了01
之碗, case test
對(duì)應(yīng)了00
蝙眶,但是感覺順序不太對(duì),明明是other
在前褪那,test
在后的幽纷,帶著這個(gè)疑問式塌,我們把用例改造如下
enum TestEnum {
case aaa
case test(Int)
case ccc
case test3(Int, Int)
case test2(Int,Int, Int)
case other
}
print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.aaa
print("t = .aaa的內(nèi)存情況: ",Mems.memStr(ofVal: &t))
t = TestEnum.test(10)
print("t = .test(10)的內(nèi)存情況: ",Mems.memStr(ofVal: &t))
t = TestEnum.ccc
print("t = .ccc的內(nèi)存情況: ",Mems.memStr(ofVal: &t))
t = TestEnum.test3(16, 32)
print("t = .test3(16, 32)的內(nèi)存情況: ",Mems.memStr(ofVal: &t))
t = TestEnum.test2(20, 20, 20)
print("t = .test2(20, 20, 20)的內(nèi)存情況:",Mems.memStr(ofVal: &t))
t = TestEnum.other
print("t = .other的內(nèi)存情況: ",Mems.memStr(ofVal: &t))
print("Stop for debug")
*************************************運(yùn)行結(jié)果
系統(tǒng)實(shí)際分配內(nèi)存 32
實(shí)際使用的內(nèi)存 25
內(nèi)存對(duì)齊參數(shù) 8
t = .aaa的內(nèi)存情況: 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000003
t = .test(10)的內(nèi)存情況: 0x000000000000000a 0x0000000000000000 0x0000000000000000 0x0000000000000000
t = .ccc的內(nèi)存情況: 0x0000000000000001 0x0000000000000000 0x0000000000000000 0x0000000000000003
t = .test3(16, 32)的內(nèi)存情況: 0x0000000000000010 0x0000000000000020 0x0000000000000000 0x0000000000000001
t = .test2(20, 20, 20)的內(nèi)存情況: 0x0000000000000014 0x0000000000000014 0x0000000000000014 0x0000000000000002
t = .other的內(nèi)存情況: 0x0000000000000002 0x0000000000000000 0x0000000000000000 0x0000000000000003
Stop for debug
Program ended with exit code: 0
從上面的調(diào)試,又挖掘了一點(diǎn)小細(xì)節(jié):
- 對(duì)于有關(guān)聯(lián)值的
成員case
友浸,它的case
值會(huì)根據(jù)定義的順序珊搀,默認(rèn)從0開始+1累加, - 其余所有不帶關(guān)聯(lián)值的
成員case
尾菇,它們的case
值相同,而且都等于最后一個(gè)可關(guān)聯(lián)成員case 的值+1
關(guān)聯(lián)值 VS 原始值rawValue
以上我們看清楚了簡(jiǎn)單枚舉囚枪、關(guān)聯(lián)值枚舉派诬、原始值枚舉在內(nèi)存中分別是如何存儲(chǔ)的,可以看出链沼,枚舉的關(guān)聯(lián)值和原始值又以下區(qū)別:
- 內(nèi)存角度:關(guān)聯(lián)值是直接存儲(chǔ)在枚舉變量?jī)?nèi)存里面的默赂,而原始值則不是,因?yàn)樵贾凳峭ㄟ^
xx.rawValue
訪問的括勺,因此它的值完全不需要存儲(chǔ)缆八,可以在枚舉定義完之后通過方法提供給外部。 - 使用角度:原始值必須在枚舉定義的時(shí)候確定原始值類型疾捍,才能被使用
enum Direction
: String/Int/...
{...}
奈辰。關(guān)聯(lián)值則必須在枚舉定義的時(shí)候,確定好case
所對(duì)應(yīng)的關(guān)聯(lián)值類型 - 賦值:關(guān)聯(lián)值只能在枚舉
case
被賦值給變量的時(shí)候進(jìn)行賦值乱豆,因?yàn)橥粋€(gè)case
每次被賦值給變量奖恰,都需要設(shè)定一個(gè)關(guān)聯(lián)值,因此也可以說關(guān)聯(lián)值是可以改變的宛裕,如下
enum Score {
case points(Int)
case grade(Character)
}
var score = Score.points(96)
score = .grade("A")
score = .grade("B") -->相同的case瑟啃,不同的關(guān)聯(lián)值
而原始值,只能在枚舉定義的時(shí)候進(jìn)行賦值揩尸,不賦值則系統(tǒng)會(huì)給定相應(yīng)的默認(rèn)值蛹屿,也就是只有一次機(jī)會(huì)可以賦值,定義完枚舉之后岩榆,就沒有辦法可以更改原始值了错负,示例如下
enum Grade: String {
case perfect = "A"
case great
case good = "C"
case bad = "D"
}
print(Grade.perfect.rawValue) --> A
print(Grade.great.rawValue) --> 定義時(shí)無賦值,系統(tǒng)默認(rèn)為case的名稱 great
print(Grade.good.rawValue) --> C
print(Grade.bad.rawValue) -> D