本節(jié),分析枚舉
enum
- 各語言枚舉區(qū)別
- swift枚舉的使用
- swift枚舉大小
- 枚舉的嵌套
- 枚舉的遞歸(indirect)
- OC橋接
- SIL分析
1. 各語言枚舉區(qū)別
1.1 C語言
枚舉
- 僅支持
Int
類型均蜜,默認(rèn)首元素
值為0囤耳,后續(xù)元素值
依次+1
充择。
如果中間
有元素賦值
腋寨,以賦值
為準(zhǔn)萄窜,后續(xù)沒賦值
的元素值
依舊依次+1
enum WEEK {
Mon, Tue = 10, Wed, Thu, Fri, Sat, Sun
};
enum WEEK a = Mon;
enum WEEK b = Wed;
printf("%d",a); // 打印0
printf("%d",b); // 打印11
OC語言
的枚舉類型
與C語言
一致
1.2 Swift
枚舉
十分強(qiáng)大
- 格式:
不用逗號
分隔查刻,類型需使用case聲明
- 內(nèi)容:
- 支持
Int
穗泵、Double
佃延、String
等基礎(chǔ)類型
夷磕,有默認(rèn)枚舉值
(String
類型默認(rèn)枚舉值
為key名
坐桩,Int绵跷、Double
數(shù)值型默認(rèn)枚舉值
為0
開始+1
遞增 )- 支持
自定義選項(xiàng)
不指定
支持類型
碾局,沒有rawValue
奴艾。但同樣支持case枚舉
握侧,可自定義關(guān)聯(lián)內(nèi)容
品擎。
- 指定類型:
沒指定
枚舉值時(shí)萄传,各類型都有默認(rèn)枚舉值
秀菱。
// Double類型
// CaseIterable協(xié)議蹭睡,有allCases屬性肩豁,支持遍歷所有case
enum Week1: Double, CaseIterable {
case Mon,Tue, Wed, Thu, Fri, Sat, Sun
}
Week1.allCases.forEach { print($0.rawValue)}
// String類型
enum Week2: String, CaseIterable {
case Mon,Tue, Wed, Thu, Fri, Sat, Sun
}
Week2.allCases.forEach { print($0.rawValue)}
- 自定義類型(
強(qiáng)大
)
不指定
枚舉類型清钥,可給枚舉項(xiàng)
添加拓展內(nèi)容
祟昭。switch
讀取時(shí)篡悟,可提取出拓展類型
,進(jìn)行相應(yīng)操作旗吁。
// 自定義類型的使用
enum Shape {
case square(width: Double)
case circle(radius: Double, borderWidth:Double)
}
func printValue(_ v: Shape) {
// switch區(qū)分case(不想每個(gè)case處理很钓,可使用default)
switch v {
case .square(let width):
print(width)
case .circle(let radius, _):
print(radius)
}
}
let s = Shape.square(width: 10)
let c = Shape.circle(radius: 20, borderWidth: 1)
printValue(s)
printValue(c)
2. swift枚舉的使用
- swift枚舉的讀取码倦,有兩種方式:
1.
統(tǒng)一
使用switch
區(qū)分case
- 判斷
單類case
袁稽,直接使用if語句
2.1 switch方式
- 靈活的
屬性讀取
:
let
聲明整個(gè)case
分開
聲明case
中的每個(gè)關(guān)聯(lián)內(nèi)容
合并case
推汽,使用同一個(gè)變量x
enum Shape {
case square(width: Double)
case circle(radius: Double, borderWidth:Double)
}
func printValue1(_ v: Shape) {
switch v {
// 1. let聲明整個(gè)case
case let .square(x):
print(x)
// 2. 分開聲明case中的每個(gè)關(guān)聯(lián)內(nèi)容
case .circle(let x, var y):
y += 100 // var聲明的變量歹撒,可修改和賦值
print("radius: \(x), borderWidth: \(y)")
}
}
func printValue2(_ v: Shape) {
switch v {
// 3. 合并case暖夭,使用同一個(gè)變量x
case let .square(x), let .circle(x, _):
print(x)
}
}
let s = Shape.square(width: 10)
let c = Shape.circle(radius: 20, borderWidth: 1)
print("------")
printValue1(s)
printValue1(c)
print("------")
printValue2(s)
printValue2(c)
2.2 if 方式
- 判斷是否為
指定case項(xiàng)
迈着,并獲取關(guān)聯(lián)內(nèi)容
裕菠。
(同樣支持整體
case關(guān)聯(lián)內(nèi)容聲明
和分開聲明
)
// 自定義類型的使用
enum Shape {
case square(width: Double)
case circle(radius: Double, borderWidth:Double)
}
let s = Shape.square(width: 10)
let c = Shape.circle(radius: 20, borderWidth: 1)
// 判斷s是否是square類型奴潘。并獲取`關(guān)聯(lián)內(nèi)容`
// 1. 內(nèi)部聲明關(guān)聯(lián)內(nèi)容類型(如: let)
if case .square(let width) = s {
print(width)
}
// 2. 聲明case所有關(guān)聯(lián)內(nèi)容類型(如: var)
if case var .circle(radius, borderWidth) = c {
radius += 200
borderWidth += 100
print(radius, borderWidth)
}
2.3 計(jì)算型屬性 & 函數(shù)
-
enum枚舉
支持計(jì)算型屬性
和函數(shù)
enum Direct: Int {
case up
case down
case left
case right
// 計(jì)算型屬性
var description: String{
switch self {
case .up:
return "這是上面"
default:
return "這是\(self)"
}
}
// 函數(shù)
func printSelf() {
print(description)
}
}
Direct.down.printSelf() // 打佑┎省: 這是down
3. swift枚舉大小
size
:實(shí)際占用
內(nèi)存大小
stride
:系統(tǒng)分配
的內(nèi)存大小
指定類型:
- 僅
一個(gè)case
項(xiàng):size
為0
(高版本xcode
可能為1
)雀扶,stride
為1
)多個(gè)case
項(xiàng):
case小于255
個(gè):size
為1
愚墓,stride
為1
超過255
個(gè)會自動擴(kuò)容
浪册,size
和stride
都會增加
村象。
(原因,1字節(jié)
(8bit)可區(qū)分255種
情況躁劣。所以默認(rèn)size
為1
账忘,當(dāng)只有一個(gè)case
時(shí)鳖擒,0x0
即可表示
蒋荚。所以size
為0
和1
都可理解)
自定義:
占用內(nèi)存空間最大
的case
大小 +enum
自身大小:
如果不清楚Foo2
的case
大小size
為何為18
圆裕,可查看內(nèi)存對齊
順帶提供一個(gè)
struct(5個(gè)屬性值)
大小計(jì)算方式
:
4. 枚舉的嵌套
-
枚舉的嵌套
,本質(zhì)上
只是在不同作用域
內(nèi)創(chuàng)建
吨铸,并沒有造成結(jié)構(gòu)上
的嵌套
诞吱。
4.1 enum嵌套enum
enum Foo {
enum Direct: Int {
case up
case down
case left
case right
}
case leftUp(element1: Direct, element2: Direct)
case rightDown(element1: Direct, element2: Direct)
}
var f = Foo.leftUp(element1: .left, element2: .up)
4.2 struct嵌套enum
struct Foo {
enum Direct: Int {
case up
case down
case left
case right
}
let key: Direct
func printKey() {
switch key {
case .up: print("上")
case .down: print("下")
default:
print("其他")
}
}
}
var f = Foo(key: .down)
f.printKey() // 打臃课: 下
5. 枚舉的遞歸(indirect)
- 枚舉中
case關(guān)聯(lián)內(nèi)容
使用自己枚舉類型
咙俩,是否會造成遞歸
阿趁?枚舉
的大小
如何確定脖阵?
案例:
樹節(jié)點(diǎn)
,需要重復(fù)使用:
直接使用
,XCode
會報(bào)錯(cuò)
卵史。
(因?yàn)?code>直接使用以躯,enum
的大小
需要case
來確定
啄踊,而case
的大小
又需要使用到enum大小
颠通。所以無法計(jì)算大小
顿锰,報(bào)錯(cuò))
-
swift
提供了indirect
關(guān)鍵字硼控,可以標(biāo)記遞歸枚舉
牢撼,也可以標(biāo)記單個(gè)case
,被標(biāo)記后纷责,case項(xiàng)
直接去堆中申請內(nèi)存
再膳,變?yōu)?code>引用類型喂柒,大小為指針8字節(jié)
。
- 輸出
SIL中間代碼
,可以看到是使用alloc_box
創(chuàng)建枚舉值吠撮,內(nèi)部調(diào)用了swift_allocObject
,去堆
中申請空間
:
在SIL官方文檔中泥兰,有介紹
alloc_box
:
從
匯編層
也可佐證
:
6.OC橋接
-
OC
枚舉僅支持Int
類型,而swift
支持多種類型
迈嘹。
6.1 OC
使用swift
枚舉:
swift
中創(chuàng)建Int
類型枚舉值
秀仲,使用@objc
聲明
@objc
聲明后神僵,橋接文件
中保礼,自動生成了OC
的SWIFT_ENUM
:
OC
文件中炮障,導(dǎo)入swift
橋接頭文件铝阐,直接調(diào)用SwiftEnum
6.2 swift
使用OC
枚舉:
OC
的.h頭文件
聲明枚舉類型:
typedef NS_ENUM(NSUInteger, OCEnum)
:自動轉(zhuǎn)換成swift枚舉
typedef enum
:轉(zhuǎn)換成結(jié)構(gòu)體
橋接文件
中,添加OC
頭文件:
自動生成
的swift
文件中吹害,可以看到轉(zhuǎn)換的類型:
swift
文件中使用:
NS_ENUM
生成:可正常使用
typedef enum
生成:只能通過通過值初始化
,再使用
6.3 OC
使用swift
枚舉:
- 如果
swift
中不是Int
類型纵穿,而又希望OC能用
谓媒,只能自己做個(gè)橋接
句惯。
(例如: 原本
swift
中枚舉類型
為String
抢野,可直接通過rawValue
讀取值指孤。
為了兼容OC
,把類型改成Int
逝嚎,自定義計(jì)算型屬性
补君,禁止使用默認(rèn)的rawValue
讀让粱ァ)
swift
中創(chuàng)建int類型
的枚舉
敞掘,自定義string
計(jì)算屬性玖雁,并禁止rawValue
的使用赫冬。
OC
中直接使用
:
- 如果還希望
OC
能訪問到swift
對應(yīng)的String
值:
使用
class
的類方法兼容
:
class
需要繼承NSObject
膛薛,類函數(shù)
完成枚舉
與String
的映射补鼻。enum
禁止rawValue
雅任,改用string
計(jì)算屬性獲取
:
橋接文件
中可以看到生成了OC
的類方法
和枚舉
:
OC文件
中使用:
7. SIL分析
7.1 enum格式
- 案例代碼:
enum Week: String {
case Mon
case Tue
case Wed
case Thu
case Fri
case Sat
case Sun
}
- 打開終端,
cd
到當(dāng)前文件夾
咨跌,swiftc -emit-sil main.swift > ./main.sil
命令輸出SIL
文件:
取消
swift函數(shù)名的混淆
輸出:swiftc -emit-sil main.swift | xcrun swift-demangle > ./main.sil && open main.sil
7.2 rawValue的讀取
- 在
SIL文件
中沪么,搜索rawValue
的getter
方法:
(switch
跳轉(zhuǎn)指定case
,執(zhí)行函數(shù)
,得到case內(nèi)容
锌半,返回case內(nèi)容
)
有2個(gè)疑問:
-
默認(rèn)屬性
(字符串)是什么時(shí)候創(chuàng)建
的? - 如何記錄
case名
和對應(yīng)值
的拳喻?
默認(rèn)屬性
(字符串)是什么時(shí)候創(chuàng)建
的哭当?
編譯期
就會生成所有符號
- 所以上面
rawValue
讀取時(shí),可直接通過string_literal
從MachO
中讀取字符
冗澈。
- 如何記錄
case名
和對應(yīng)值
的钦勘?
String類型
的枚舉
,case
與rawValue值
(打印結(jié)果一樣亚亲,但類型是對應(yīng)枚舉類型
和String
)- 通過
rawValue
初始化case
時(shí)彻采,類型為Option
(找不到
對應(yīng)case
時(shí),為nil
)enum Week: String { case Mon case Tue case Wed case Thu case Fri case Sat case Sun } // case與rawValue值(打印結(jié)果一樣捌归,但類型不同) print("類型:\(type(of: Week.Mon)) 值:\(Week.Mon)") // 打痈叵臁: 類型:Week 值:Mon print("類型:\(type(of: Week.Mon.rawValue)) 值:\(Week.Mon.rawValue)") // 打印: 類型:String 值:Mon // 通過rawValue來生成對應(yīng)的case(可選類型惜索,找不到rawValue對應(yīng)的case特笋,就是nil) print(Week.init(rawValue: "Mon")) // 打印: Optional(Demo.Week.Mon) print(Week.init(rawValue: "Hello")) // 打咏碚住: nil
通過
SIL
分析init(rawValue:)
:
完整流程1.創(chuàng)建:
創(chuàng)建枚舉
(格式:元組(Array<T>,Pointer)
猎物,此例中T
為String
) ,再遍歷創(chuàng)建
所有枚舉
項(xiàng)角塑。2.查詢:
通過_findStringSwitchCase
獲取入?yún)⒅?/code>的
index
蔫磨,使用switch
通過index
(int類型)匹配到case
,匹配成功
:返回optiona
l的some值
圃伶,匹配失敗
堤如,直接返回nil
在
swift
源碼中搜索findStringSwitchCase
,可以看到是通過for遍歷
匹配index
:
enum枚舉
較為簡單
窒朋,我們了解了
與其他語言
的差異
搀罢、用法
,順帶探索大小
和源碼實(shí)現(xiàn)
炼邀。
- 下一節(jié)魄揉,介紹swift
閉包
。