C語(yǔ)?枚舉
先來(lái)回顧?下
C
語(yǔ)?的枚舉寫法:
enum 枚舉名 {
枚舉值1,
枚舉值2,
......
};
?如表示?周 7天,?
C
語(yǔ)?的枚舉寫法應(yīng)該是這樣的:
enum week {
MON, TUE, WED, THU, FRI, SAT, SUN
};
第?個(gè)枚舉成員默認(rèn)值為
0
平窘,后?枚舉值依次類推。如果更改只需這樣操作:
enum week {
MON = 1, TUE, WED, THU, FRI, SAT, SUN
};
定義?個(gè)枚舉變量
week
:
enum Week {
MON = 1, TUE, WED, THU, FRI, SAT, SUN
} week;
同樣是定義?個(gè)枚舉變量
week
,省略枚舉名稱:
enum {
MON = 1, TUE, WED, THU, FRI, SAT, SUN
} week;
Swift中的枚舉
枚舉基礎(chǔ)
?如表示?周 7天,?
Swift
的枚舉寫法應(yīng)該是這樣的:
enum Week {
case MONDAY
case TUEDAY
case WEDDAY
case THUDAY
case FRIDAY
case SATDAY
case SUNDAY
}
上述代碼也可以直接?個(gè)
case
夭咬,然后?逗號(hào)隔開(kāi):
enum Week {
case MON, TUE, WED, THU, FRI, SAT, SUN
}
Swift
中可以創(chuàng)建String
類型枚舉。在Swift
中=
左邊的值叫枚舉值铆隘,右邊的叫rawValue
原始值卓舵,case 枚舉值 = rawValue原始值
enum Week: String {
case MON = "MON"
case TUE = "TUE"
case WED = "WED"
case THU = "THU"
case FRI = "FRI"
case SAT = "SAT"
case SUN = "SUN"
}
如果不想寫后?的字符串,這時(shí)可以使?隱?
rawValue
分配膀钠。未指定類型的枚舉掏湾,使用rawValue
屬性,編譯報(bào)錯(cuò)
未指定類型的枚舉
Int
類型枚舉肿嘲,枚舉值FRI
分配rawValue
為10
融击,MON
依然是從0
開(kāi)始,后?枚舉值依次類推雳窟。在FRI
之后的枚舉值從11
開(kāi)始尊浪,依次類推
enum Week: Int {
case MON, TUE, WED, THU, FRI = 10, SAT, SUN
}
print("MON:\(Week.MON.rawValue),SAT:\(Week.SAT.rawValue)")
//輸出以下內(nèi)容:
//MON:0,SAT:11
String
類型枚舉,枚舉值FRI
分配rawValue
打印Hello
封救,其他未分配rawValue
打印自身枚舉值
enum Week: String {
case MON, TUE, WED, THU, FRI = "Hello", SAT, SUN
}
print("MON:\(Week.MON.rawValue),FRI:\(Week.FRI.rawValue),SUN:\(Week.SUN.rawValue)")
//輸出以下內(nèi)容:
//MON:MON,FRI:Hello,SUN:SUN
通過(guò)SIL代碼拇涤,分析
String
類型枚舉,是如何打印rawValue
的
enum Week: String {
case MON, TUE, WED, THU, FRI, SAT, SUN
}
let w: Week = .MON
將上述代碼生成SIL文件:
swiftc -emit-sil main.swift | xcrun swift-demangle
enum Week : String {
case MON, TUE, WED, THU, FRI, SAT, SUN
typealias RawValue = String
init?(rawValue: String)
var rawValue: String { get }
}
SIL代碼的枚舉聲明除了
case
還多了一些東西:
- 首先通過(guò)
typealias
取別名誉结,在枚舉Week
里把String
取名為RawValue
- 生成可選的初始化方法
init?(rawValue: String)
工育,也就是說(shuō)初始化可以返回nil
- 生成
rawValue
計(jì)算屬性,所以在代碼中訪問(wèn)rawValue
屬性搓彻,本質(zhì)就是訪問(wèn)它的get
方法
// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
alloc_global @main.w : Swift.String // id: %2
%3 = global_addr @main.w : Swift.String : $*String // user: %8
%4 = metatype $@thin Week.Type
%5 = enum $Week, #Week.MON!enumelt // user: %7
// function_ref Week.rawValue.getter
%6 = function_ref @main.Week.rawValue.getter : Swift.String : $@convention(method) (Week) -> @owned String // user: %7
%7 = apply %6(%5) : $@convention(method) (Week) -> @owned String // user: %8
store %7 to %3 : $*String // id: %8
%9 = integer_literal $Builtin.Int32, 0 // user: %10
%10 = struct $Int32 (%9 : $Builtin.Int32) // user: %11
return %10 : $Int32 // id: %11
} // end sil function 'main'
main
方法:
%5
接收枚舉值%6
獲取Week.rawValue.getter
方法地址- 通過(guò)
apply
調(diào)用getter
方法%6
,傳入?yún)?shù)枚舉值%5
嘱朽,將返回值賦值給%7
- 將返回結(jié)果
%7
存儲(chǔ)到%3
// Week.rawValue.getter
sil hidden @main.Week.rawValue.getter : Swift.String : $@convention(method) (Week) -> @owned String {
// %0 "self" // users: %2, %1
bb0(%0 : $Week):
debug_value %0 : $Week, let, name "self", argno 1 // id: %1
switch_enum %0 : $Week, case #Week.MON!enumelt: bb1, case #Week.TUE!enumelt: bb2, case #Week.WED!enumelt: bb3, case #Week.THU!enumelt: bb4, case #Week.FRI!enumelt: bb5, case #Week.SAT!enumelt: bb6, case #Week.SUN!enumelt: bb7 // id: %2
bb1: // Preds: bb0
%3 = string_literal utf8 "MON" // user: %8
%4 = integer_literal $Builtin.Word, 3 // user: %8
%5 = integer_literal $Builtin.Int1, -1 // user: %8
%6 = metatype $@thin String.Type // user: %8
// function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
%7 = function_ref @Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %8
%8 = apply %7(%3, %4, %5, %6) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %9
br bb8(%8 : $String) // id: %9
bb2: // Preds: bb0
%10 = string_literal utf8 "TUE" // user: %15
%11 = integer_literal $Builtin.Word, 3 // user: %15
%12 = integer_literal $Builtin.Int1, -1 // user: %15
%13 = metatype $@thin String.Type // user: %15
// function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
%14 = function_ref @Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %15
%15 = apply %14(%10, %11, %12, %13) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %16
br bb8(%15 : $String) // id: %16
bb3: // Preds: bb0
%17 = string_literal utf8 "WED" // user: %22
%18 = integer_literal $Builtin.Word, 3 // user: %22
%19 = integer_literal $Builtin.Int1, -1 // user: %22
%20 = metatype $@thin String.Type // user: %22
// function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
%21 = function_ref @Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %22
%22 = apply %21(%17, %18, %19, %20) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %23
br bb8(%22 : $String) // id: %23
bb4: // Preds: bb0
%24 = string_literal utf8 "THU" // user: %29
%25 = integer_literal $Builtin.Word, 3 // user: %29
%26 = integer_literal $Builtin.Int1, -1 // user: %29
%27 = metatype $@thin String.Type // user: %29
// function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
%28 = function_ref @Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %29
%29 = apply %28(%24, %25, %26, %27) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %30
br bb8(%29 : $String) // id: %30
bb5: // Preds: bb0
%31 = string_literal utf8 "FRI" // user: %36
%32 = integer_literal $Builtin.Word, 3 // user: %36
%33 = integer_literal $Builtin.Int1, -1 // user: %36
%34 = metatype $@thin String.Type // user: %36
// function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
%35 = function_ref @Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %36
%36 = apply %35(%31, %32, %33, %34) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %37
br bb8(%36 : $String) // id: %37
bb6: // Preds: bb0
%38 = string_literal utf8 "SAT" // user: %43
%39 = integer_literal $Builtin.Word, 3 // user: %43
%40 = integer_literal $Builtin.Int1, -1 // user: %43
%41 = metatype $@thin String.Type // user: %43
// function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
%42 = function_ref @Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %43
%43 = apply %42(%38, %39, %40, %41) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %44
br bb8(%43 : $String) // id: %44
bb7: // Preds: bb0
%45 = string_literal utf8 "SUN" // user: %50
%46 = integer_literal $Builtin.Word, 3 // user: %50
%47 = integer_literal $Builtin.Int1, -1 // user: %50
%48 = metatype $@thin String.Type // user: %50
// function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
%49 = function_ref @Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %50
%50 = apply %49(%45, %46, %47, %48) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %51
br bb8(%50 : $String) // id: %51
// %52 // user: %53
bb8(%52 : $String): // Preds: bb7 bb6 bb5 bb4 bb3 bb2 bb1
return %52 : $String // id: %53
} // end sil function 'main.Week.rawValue.getter : Swift.String'
Week.rawValue.getter
方法:
bb0
旭贬,接收一個(gè)枚舉值,self
就是該枚舉值搪泳,通過(guò)switch_enum
匹配跳轉(zhuǎn)到對(duì)應(yīng)bb1
至bb7
代碼分支bb1
至bb7
稀轨,構(gòu)建出對(duì)應(yīng)枚舉值的字符串,最終都調(diào)用bb8
bb8
返回對(duì)應(yīng)字符串
rawValue.getter
返回的字符串在編譯時(shí)期已經(jīng)存儲(chǔ)好岸军,通過(guò)Mach-O
文件查看它們的存儲(chǔ)位置
Mach-O
rawValue
的字符串存儲(chǔ)在__TEXT.__cstring
段奋刽,而且內(nèi)存地址是連續(xù)的。在上述SIL代碼中構(gòu)建枚舉值字符串艰赞,本質(zhì)就是從Mach-O
里把對(duì)應(yīng)地址的字符串取出來(lái)
區(qū)分case和rawValue
enum Week: String {
case MON, TUE, WED, THU, FRI, SAT, SUN
}
print("case枚舉值:\(Week.MON)")
print("rawValue原始值:\(Week.MON.rawValue)")
//輸出以下內(nèi)容:
//case枚舉值:MON
//rawValue原始值:MON
上述代碼中佣谐,
case
枚舉值Week.MON
和rawValue
原始值Week.MON.rawValue
打印出的內(nèi)容完全一致,但一個(gè)是輸出枚舉值方妖,一個(gè)是訪問(wèn)rawValue
的get
方法狭魂,它們是完全不同的東西
做一個(gè)簡(jiǎn)單的測(cè)試,如果將編譯報(bào)錯(cuò)String
類型的Week.MON.rawValue
賦值給Week
類型week1
,再將Week
類型Week.MON
賦值給String
類型week2
雌澄,都會(huì)編譯報(bào)錯(cuò)
init方法的調(diào)用時(shí)機(jī)
添加符號(hào)斷點(diǎn)
Week.init
斋泄,預(yù)期斷住枚舉初始化方法
Symbol
enum Week: String {
case MON, TUE, WED, THU, FRI, SAT, SUN
}
let week: Week = Week.MON
print("case枚舉值:\(week)")
print("rawValue原始值:\(week.rawValue)")
//輸出以下內(nèi)容:
//case枚舉值:MON
//rawValue原始值:MON
運(yùn)行上述代碼,沒(méi)有達(dá)到預(yù)期镐牺,沒(méi)有任何斷點(diǎn)被觸發(fā)
enum Week: String {
case MON, TUE, WED, THU, FRI, SAT, SUN
}
print(Week(rawValue: "MON"))
print(Week.init(rawValue: "TUE"))
print(Week(rawValue: "Hello"))
//輸出以下內(nèi)容:
//Optional(LGSwiftTest.Week.MON)
//Optional(LGSwiftTest.Week.TUE)
//nil
修改上述代碼炫掐,使用
Week(rawValue:)
或Week.init(rawValue:)
成功觸發(fā)斷點(diǎn)
Week.init繼續(xù)執(zhí)行代碼,從運(yùn)行結(jié)果來(lái)看睬涧,前兩個(gè)打印
Optional
可選值募胃,第三個(gè)打印nil
,因?yàn)檎也坏綄?duì)應(yīng)Hello
的枚舉值
運(yùn)行結(jié)果
通過(guò)SIL代碼宙地,分析枚舉的初始化構(gòu)造函數(shù)
enum Week: String {
case MON, TUE, WED, THU, FRI, SAT, SUN
}
print(Week(rawValue: "Hello"))
將上述代碼生成SIL文件:
swiftc -emit-sil main.swift | xcrun swift-demangle
來(lái)到
Week.init(rawValue:)
方法
init來(lái)到
bb1
方法
bb1從
bb1
至bb14
摔认,將所有字符串存儲(chǔ)到數(shù)組內(nèi),進(jìn)行case
匹配
bb14通過(guò)源碼查看
_findStringSwitchCase
方法宅粥,兩個(gè)入?yún)⒎謩e是數(shù)組和需要匹配的字符串参袱,然后遍歷數(shù)組,如果匹配成功返回對(duì)應(yīng)index
秽梅,如果匹配失敗返回-1
_findStringSwitchCase
bb15
匹配成功抹蚀,直接跳轉(zhuǎn)bb29
。bb16
匹配失敗企垦,繼續(xù)后續(xù)的匹配
bb15环壤、bb16
bb28
全部匹配失敗,構(gòu)建一個(gè).none
類型的Optional
返回钞诡,就是nil
郑现。bb29
匹配成功,構(gòu)建一個(gè).some
類型的Optional
返回荧降,就是對(duì)應(yīng)的枚舉值
bb28接箫、bb29
關(guān)聯(lián)值枚舉
如果想?枚舉表達(dá)更復(fù)雜的信息,?不僅僅是?個(gè)
rawValue
這么簡(jiǎn)單朵诫,這個(gè)時(shí)候可以使?關(guān)聯(lián)值
enum Shape {
case circle(radious: Double)
case rectangle(width: Int, height: Int)
}
關(guān)聯(lián)值枚舉辛友,沒(méi)有
rawValue
屬性。因?yàn)殛P(guān)聯(lián)值枚舉可以使用單個(gè)值或一組值來(lái)表示剪返,但rawValue
只能針對(duì)單個(gè)值
將上述代碼生成SIL文件:
swiftc -emit-sil main.swift | xcrun swift-demangle
只有關(guān)聯(lián)值枚舉case
和關(guān)聯(lián)值废累,沒(méi)有typealias
取別名,沒(méi)有init
方法脱盲,沒(méi)有rawValue
計(jì)算屬性
關(guān)聯(lián)值枚舉可以省略關(guān)聯(lián)值的標(biāo)簽邑滨,例如
radious
、width
钱反、height
驼修,但并不推薦這種書(shū)寫方式殿遂,因?yàn)榭勺x性太差
enum Shape {
case circle(Double)
case rectangle(Int, Int)
}
關(guān)聯(lián)值枚舉的使用
//創(chuàng)建
var circle = Shape.circle(radious: 10.0)
//重新分配
circle=Shape.circle(radious: 20.0)
模式匹配
使?
switch
匹配enum
的時(shí)候,必須列舉當(dāng)前所有可能的情況乙各,否則編譯報(bào)錯(cuò)
編譯報(bào)錯(cuò)
匹配
enum
可以列舉出所有情況墨礁,也可以使用default
表示默認(rèn)情況
enum Week: String {
case MON = "MON"
case TUE = "TUE"
case WED = "WED"
case THU = "THU"
case FRI = "FRI"
case SAT = "SAT"
case SUN = "SUN"
}
var week: Week?
switch week {
case .MON:
print(Week.MON.rawValue)
default:
print("unknow day")
}
將上述代碼生成SIL文件:
swiftc -emit-sil main.swift | xcrun swift-demangle
SIL
匹配關(guān)聯(lián)值枚舉
- 方式一:通過(guò)
switch
匹配所有case
enum Shape {
case circle(radious: Double)
case rectangle(width: Int, height: Int)
}
var shape = Shape.circle(radious: 10.0)
switch shape {
case let .circle(radious):
print("circle-radious:\(radious)")
case let .rectangle(width, height):
print("rectangle-width:\(width),height:\(height)")
}
//輸出以下內(nèi)容:
//circle-radious:10.0
case let .circle(radious)
相當(dāng)于做了value-binding
耳峦,如果case
匹配上恩静,相當(dāng)于把10.0
賦值給常量radious
另一種寫法:將關(guān)聯(lián)值的參數(shù)使用
let
、var
修飾
switch shape {
case .circle(var radious):
print("circle-radious:\(radious)")
case .rectangle(let width, let height):
print("rectangle-width:\(width)蹲坷,height:\(height)")
}
通過(guò)SIL代碼驶乾,查看關(guān)聯(lián)值枚舉的匹配模式
enum Shape {
case circle(radious: Double)
case rectangle(width: Int, height: Int)
}
var shape = Shape.circle(radious: 10.0)
var temR: Double
var w: Int
var h: Int
switch shape {
case .circle(let radious):
temR=radious
case .rectangle(let width, let height):
w=width
h=height
}
將上述代碼生成SIL文件:
swiftc -emit-sil main.swift | xcrun swift-demangle
main
bb1
- 方式二:通過(guò)單個(gè)
case
進(jìn)行匹配
enum Shape {
case circle(radious: Double)
case rectangle(width: Int, height: Int)
}
var shape = Shape.circle(radious: 10.0)
if case let Shape.circle(radious) = shape {
print("circle-radious:\(radious)")
}
//輸出以下內(nèi)容:
//circle-radious:10.0
匹配不同枚舉值的相同關(guān)聯(lián)值
enum Shape {
case circle(radious: Double, diameter: Double)
case rectangle(width: Double, height: Double)
case square(width: Double, width: Double)
}
var shape = Shape.circle(radious: 10.0, diameter: 20.0)
switch shape {
case let .circle(x, 20.0), let .square(x, 20.0):
print("x:\(x)")
default:
print("default")
}
//輸出以下內(nèi)容:
//x:10.0
上述代碼,將多個(gè)枚舉值中循签,我們想要匹配的關(guān)聯(lián)值用
x
代替级乐。如果枚舉值為circle
或square
,且第二個(gè)關(guān)聯(lián)值為20.0
县匠,即為匹配成功
通過(guò)SIL代碼风科,查看不同枚舉值的相同關(guān)聯(lián)值是如何匹配的
enum Shape {
case circle(radious: Double, diameter: Double)
case rectangle(width: Double, height: Double)
case square(width: Double, width: Double)
}
var shape = Shape.circle(radious: 10.0, diameter: 20.0)
var tmpR: Double
switch shape {
case let .circle(x, 20.0), let .square(x, 20.0):
tmpR=x
default:
print("default")
}
將上述代碼生成SIL文件:
swiftc -emit-sil main.swift | xcrun swift-demangle
mainbb1、bb2乞旦、bb3不同枚舉值里贼穆,用到匹配的變量或常量
x
,必須名稱相同兰粉,不能一個(gè)用x
一個(gè)用y
故痊,否則編譯報(bào)錯(cuò)
編譯報(bào)錯(cuò)
使用通配符
_
方式匹配
enum Shape {
case circle(radious: Double, diameter: Double)
case rectangle(width: Double, height: Double)
case square(width: Double, width: Double)
}
var shape = Shape.circle(radious: 10.0, diameter: 30.0)
switch shape {
case let .circle(x, _), let .square(x, _):
print("x:\(x)")
default:
print("default")
}
//輸出以下內(nèi)容:
//x:10.0
上述代碼,我們不關(guān)心第二個(gè)關(guān)聯(lián)值是什么玖姑,可以使用通配符
_
代替愕秫。如果枚舉值為circle
或square
,第二個(gè)關(guān)聯(lián)為任意值焰络,都能匹配成功
同樣使用單個(gè)
case
進(jìn)行匹配豫领,也可以使用通配符_
var shape = Shape.circle(radious: 10.0, diameter: 20.0)
if case let Shape.circle(x, _) = shape{
print("x:\(x)")
}
//輸出以下內(nèi)容:
//x:10.0
枚舉嵌套
enum CombineDirect{
enum BaseDirect{
case up
case down
case left
case right
}
case leftUp(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
case rightUp(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
case leftDown(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
case rightDown(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
}
var combind = CombineDirect.leftDown(baseDirect1: .left, baseDirect2: .down)
上述代碼,通過(guò)
BaseDirect
枚舉的up
舔琅、down
、left
洲劣、right
四個(gè)case
备蚓,組合出CombineDirect
枚舉中leftUp
、rightUp
囱稽、leftDown
郊尝、rightDown
四個(gè)case
。這種方式下BaseDirect
枚舉相當(dāng)于是私有的战惊,外界無(wú)法直接訪問(wèn)
結(jié)構(gòu)體中嵌套枚舉
struct Skill{
enum KeyType{
case up
case down
case left
case right
}
let key: KeyType
func launchSkill(){
switch key {
case .left,.right:
print("left, right")
case .up,.down:
print("up, down")
}
}
}
let kill = Skill.init(key: .up)
kill.launchSkill()
//輸出以下內(nèi)容:
//up, down
枚舉中包含屬性
enum
中能包含計(jì)算屬性流昏,類型屬性,不能包含存儲(chǔ)屬性
enum Shape {
case circle(radius: Double)
case rectangle(width: Double, height: Double)
var width: Double{
get{
return 10.0
}
}
static let height = 20.0
}
enum
中包含存儲(chǔ)屬性,編譯報(bào)錯(cuò)
編譯報(bào)錯(cuò)
結(jié)構(gòu)體可以包含存儲(chǔ)屬性况凉,因?yàn)榻Y(jié)構(gòu)體的大小就是存儲(chǔ)屬性的大小谚鄙。但enum
大小取決于case
的個(gè)數(shù),只要case
個(gè)數(shù)沒(méi)有超過(guò)255
刁绒,enum
的大小就是1字節(jié)
- 計(jì)算屬性本質(zhì)是
get
闷营、set
方法,對(duì)于值類型來(lái)說(shuō)根本不用存儲(chǔ)方法- 類型屬性是全局變量知市,它的存儲(chǔ)也和
enum
沒(méi)有任何關(guān)系
枚舉中包含?法
可以在
enum
中定義實(shí)例?法傻盟,static
修飾的?法
enum Week: Int {
case MON, TUE, WED, THU, FRI, SAT, SUN
mutating func nextDay(){
if self == .SUN {
self = Week(rawValue: 0)!
}
else {
self = Week(rawValue: self.rawValue + 1)!
}
}
}
var week: Week = .SUN
week.nextDay()
print(week)
在
enum
的nextDay
方法中修改自身,需要使用mutating
關(guān)鍵字修飾
枚舉的大小
rawValue
枚舉值大小
enum NoMean{
case a
}
print("stride:\(MemoryLayout<NoMean>.stride)")
print("size:\(MemoryLayout<NoMean>.size)")
//輸出以下內(nèi)容:
//stride:1
//size:0
enum
中只有一個(gè)case
嫂丙,大小為0娘赴,步長(zhǎng)為1。當(dāng)只有一個(gè)case
的枚舉跟啤,大小為0表示這個(gè)enum
是沒(méi)有意義的
enum NoMean{
case a
case b
}
print("stride:\(MemoryLayout<NoMean>.stride)")
print("size:\(MemoryLayout<NoMean>.size)")
//輸出以下內(nèi)容:
//stride:1
//size:1
enum
中有兩個(gè)case
诽表,大小為1,步長(zhǎng)為1
enum NoMean{
case a
case b
case c
case d
case e
}
print("stride:\(MemoryLayout<NoMean>.stride)")
print("size:\(MemoryLayout<NoMean>.size)")
//輸出以下內(nèi)容:
//stride:1
//size:1
enum
中存在更多case
腥光,依然是大小為1关顷,步長(zhǎng)為1
將枚舉值
a
、b
武福、c
賦值給三個(gè)常量
賦值通過(guò)斷點(diǎn)查看匯編代碼议双,分析枚舉值
a
、b
捉片、c
通過(guò)斷點(diǎn)可以看出匯編代碼a
平痰、b
、c
分別是0x0
伍纫、0x1
宗雇、0x2
,對(duì)系統(tǒng)來(lái)說(shuō)就是0
莹规、1
赔蒲、2
。所以rawValue
枚舉值默認(rèn)是UInt8
類型良漱,占1字節(jié)
舞虱,最大可以存儲(chǔ)255
。超過(guò)255
個(gè)枚舉值母市,系統(tǒng)會(huì)將UInt8
升級(jí)為UInt16
矾兜、UInt32
、UInt64
通過(guò)
lldb
查看內(nèi)存
當(dāng)前枚舉的步?是lldb1字節(jié)
患久,也就意味著如果在內(nèi)存中連續(xù)存儲(chǔ)NoMean
椅寺,需要跨越1字節(jié)
的?度浑槽。1字節(jié)
也就是8 位
,最?可以表達(dá)的數(shù)字是255
關(guān)聯(lián)值枚舉的大小
enum Shape {
case circle(radius: Double)
case rectangle(width: Double, height: Double)
}
print("stride:\(MemoryLayout<Shape>.stride)")
print("size:\(MemoryLayout<Shape>.size)")
//輸出以下內(nèi)容:
//stride:24
//size:17
關(guān)聯(lián)值枚舉??返帕,取決于最?關(guān)聯(lián)值??桐玻,并加上
1字節(jié)
枚舉值大小。
circle
有一個(gè)Double
類型關(guān)聯(lián)值溉旋,占8字節(jié)
rectangle
有兩個(gè)Double
類型關(guān)聯(lián)值畸冲,占16字節(jié)
enum
的大小,就是最大關(guān)聯(lián)值16字節(jié)
观腊,再加枚舉值1字節(jié)
邑闲,共占17字節(jié)
stride
由于8字節(jié)
對(duì)齊,所以自動(dòng)補(bǔ)齊到24字節(jié)
通過(guò)
lldb
查看內(nèi)存
lldb
枚舉嵌套的大小
enum CombineDirect{
enum BaseDirect{
case up
case down
case left
case right
}
case leftUp(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
case rightUp(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
case leftDown(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
case rightDown(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
}
print("stride:\(MemoryLayout<CombineDirect>.stride)")
print("size:\(MemoryLayout<CombineDirect>.size)")
//輸出以下內(nèi)容:
//stride:2
//size:2
枚舉嵌套和關(guān)聯(lián)值枚舉一樣梧油,同樣取決于關(guān)聯(lián)值大小
通過(guò)
lldb
查看內(nèi)存
lldb
BaseDirect
中的up
苫耸、down
、left
儡陨、right
對(duì)應(yīng)的枚舉值分別為0
褪子、1
、2
骗村、3
CombineDirect
中的leftUp
嫌褪、rightUp
、leftDown
胚股、rightDown
對(duì)應(yīng)的枚舉值分別為0
笼痛、4
、8
琅拌、12
缨伊。這里并沒(méi)有規(guī)律可尋,如果加入更多case
进宝,也會(huì)變成0
刻坊、1
、2
党晋、3
...向后遞增谭胚,通過(guò)源碼分析目前還未找到相關(guān)定義- 圖中輸出的
02
是left
的枚舉值,81
要拆開(kāi)來(lái)看未玻,8
是leftDown
的枚舉值灾而,1
是down
的枚舉值enum
大小占2字節(jié)
,因?yàn)?code>leftDown的枚舉值和down
的枚舉值存儲(chǔ)在同一字節(jié)內(nèi)深胳,屬于系統(tǒng)優(yōu)化
結(jié)構(gòu)體中嵌套枚舉的大小
struct Skill{
enum KeyType{
case up
case down
case left
case right
}
let key: KeyType
}
print("stride:\(MemoryLayout<Skill>.stride)")
print("size:\(MemoryLayout<Skill>.size)")
//輸出以下內(nèi)容:
//stride:1
//size:1
結(jié)構(gòu)體中有一個(gè)
KeyType
枚舉類型的成員變量key
,所以結(jié)構(gòu)體大小為1铜犬,步長(zhǎng)為1
indirect關(guān)鍵字
如果想要表達(dá)的
enum
是?個(gè)復(fù)雜的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)舞终,可以通過(guò)indrect
關(guān)鍵字讓當(dāng)前的enum
更簡(jiǎn)潔
創(chuàng)建鏈表結(jié)構(gòu)
enum
轻庆,對(duì)應(yīng)當(dāng)前遞歸枚舉來(lái)說(shuō),不添加indirect
關(guān)鍵字敛劝,編譯報(bào)錯(cuò)
編譯報(bào)錯(cuò)因?yàn)?code>enum是值類型余爆,它會(huì)在編譯時(shí)期確定大小。但對(duì)于接收泛型
T
的enum
夸盟,編譯時(shí)期無(wú)法確定enum
大小蛾方,系統(tǒng)無(wú)法分配空間
enum List<T>{
case end
indirect case node(T, next: List<T>)
}
上述代碼,在
case node
前面添加indirect
關(guān)鍵字上陕,可以編譯通過(guò)
indirect enum List<T>{
case end
case node(T, next: List<T>)
}
另一種方式桩砰,可以在
enum List<T>
前面添加indirect
關(guān)鍵字,同樣可以編譯通過(guò)
indirect enum List<T>{
case end
case node(T, next: List<T>)
}
print("List<Int> stride:\(MemoryLayout<List<Int>>.stride)")
print("List<Int> size:\(MemoryLayout<List<Int>>.size)")
print("List<String> stride:\(MemoryLayout<List<String>>.stride)")
print("List<String> size:\(MemoryLayout<List<String>>.size)")
上述代碼释簿,分別將
Int
和String
傳入enum
亚隅,打印出來(lái)的大小都是8字節(jié)
,下面來(lái)分析一下原因
通過(guò)
lldb
查看內(nèi)存
打印case end
庶溶,存儲(chǔ)的是end
枚舉值
end打印
case node
煮纵,存儲(chǔ)的是堆區(qū)地址
node
indirect
關(guān)鍵字本質(zhì)就是通知編譯器,當(dāng)前enum
是遞歸枚舉偏螺,無(wú)法確定大小行疏,需要在堆區(qū)空間分配內(nèi)存,并存儲(chǔ)enum
通過(guò)SIL代碼套像,查看
indirect
關(guān)鍵字酿联,如何分配堆區(qū)內(nèi)存空間
indirect enum List<T>{
case end
case node(T, next: List<T>)
}
var node = List<Int>.node(10, next: List<Int>.end)
將上述代碼生成SIL文件:
swiftc -emit-sil main.swift | xcrun swift-demangle
main通過(guò)斷點(diǎn)查看匯編代碼,確實(shí)執(zhí)行了
swift_allocObject
lldb
枚舉-Swift和OC混編
OC調(diào)用Swift的枚舉
OC
只能調(diào)用Swift
中Int
類型枚舉
@objc enum NoMean: Int{
case a
case b
case c
case d
}
通過(guò)
@objc
聲明后凉夯,橋接文件中自動(dòng)生成SWIFT_ENUM
typedef SWIFT_ENUM(NSInteger, NoMean, closed) {
NoMeanA = 0,
NoMeanB = 1,
NoMeanC = 2,
NoMeanD = 3,
};
在
OC
的LGTest.m
文件中可以直接調(diào)用
@implementation LGTest
- (void)test{
NoMean a = NoMeanA;
}
@end
如果
enum
不聲明類型货葬,同時(shí)使用@objc
修飾,編譯報(bào)錯(cuò)
不聲明類型如果
enum
聲明String
類型劲够,同時(shí)使用@objc
修飾震桶,編譯報(bào)錯(cuò)
聲明String類型
Swift調(diào)用OC的枚舉
OC
的LGTest.h
中,使用typedef NS_ENUM
聲明枚舉
typedef NS_ENUM(NSInteger, CEnum) {
CEnumInvalid = 0,
CEnumA = 1,
CEnumB,
CEnumC
};
在橋接文件中征绎,自動(dòng)生成
enum CEnum
public enum CEnum : Int {
case invalid = 0
case A = 1
case B = 2
case C = 3
}
OC
的LGTest.h
中蹲姐,使用typedef enum
聲明枚舉
typedef enum{
Num1,
Num2
} OCNum;
在橋接文件中,自動(dòng)生成
struct OCNum
人柿,變成了結(jié)構(gòu)體柴墩,并遵循了Equatable
和RawRepresentable
協(xié)議
public struct OCNum : Equatable, RawRepresentable {
public init(_ rawValue: UInt32)
public init(rawValue: UInt32)
public var rawValue: UInt32
}
在
Swift
的main.swift
文件中可以直接調(diào)用
let a: CEnum = .A
let b: OCNum = OCNum.init(rawValue: 1)
print("CEnum:\(a.rawValue)")
print("OCNum:\(b.rawValue)")
//輸出以下內(nèi)容:
//CEnum:1
//OCNum:1
內(nèi)存對(duì)齊 & 字節(jié)對(duì)齊
- 內(nèi)存對(duì)齊:
iOS
采用8字節(jié)
對(duì)齊方式,只會(huì)在對(duì)象初始化分配內(nèi)存時(shí)出現(xiàn)凫岖。例如malloc
绊谭、calloc
- 字節(jié)對(duì)齊:第一個(gè)數(shù)據(jù)成員放在
offset
為0
的位置新蟆,以后每個(gè)數(shù)據(jù)成員存儲(chǔ)的起始位置要從該成員大小或該成員的子成員大小(只要該成員有子成員礁扮,比如數(shù)組、結(jié)構(gòu)體等)的整數(shù)倍開(kāi)始。比如Int
為8字節(jié)
,則要從8
的整數(shù)倍地址開(kāi)始存儲(chǔ)
enum Shape {
case circle(radius: Double)
case rectangle(width: Int8, height: Int, w: Int16, h: Int32)
}
print("stride:\(MemoryLayout<Shape>.stride)")
print("size:\(MemoryLayout<Shape>.size)")
//輸出以下內(nèi)容:
//stride:24
//size:24
width
:Int8
類型,占1字節(jié)
height
:Int
類型胀茵,占8字節(jié)
w
:Int16
類型,占2字節(jié)
h
:Int32
類型挟阻,占4字節(jié)
width
占1字節(jié)
琼娘,但第二成員height
占8字節(jié)
。按字節(jié)對(duì)齊規(guī)則附鸽,height
起始位置必須是自身的整數(shù)倍脱拼,所以width
要補(bǔ)齊到8字節(jié)
。而w
占2字節(jié)
拒炎,但h
占4字節(jié)
挪拟,所以同理w
要補(bǔ)齊到4字節(jié)
。最終size
大谢髂恪:8 + 8 + 4 + 4 = 24字節(jié)