swift進(jìn)階十二:枚舉enum

swift進(jìn)階 學(xué)習(xí)大綱

本節(jié),分析枚舉enum

  1. 各語言枚舉區(qū)別
  2. swift枚舉的使用
  3. swift枚舉大小
  4. 枚舉的嵌套
  5. 枚舉的遞歸(indirect)
  6. OC橋接
  7. 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)大

    1. 格式: 不用逗號分隔查刻,類型需使用case聲明
    1. 內(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)}
image.png
  • 自定義類型(強(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)
image.png

2. swift枚舉的使用

  • swift枚舉的讀取码倦,有兩種方式:

1.統(tǒng)一使用switch區(qū)分case

  1. 判斷單類case袁稽,直接使用if語句

2.1 switch方式

  • 靈活的屬性讀取
  1. let聲明整個(gè)case
  2. 分開聲明case中的每個(gè)關(guān)聯(lián)內(nèi)容
  3. 合并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)
image.png

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)
}
image.png

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):size0高版本xcode可能為1 )雀扶,stride1
  • 多個(gè)case項(xiàng):
    case小于255個(gè): size1愚墓,stride1
    超過255個(gè)會自動擴(kuò)容浪册,sizestride都會增加村象。
    (原因,1字節(jié)(8bit)可區(qū)分255種情況躁劣。所以默認(rèn)size1账忘,當(dāng)只有一個(gè)case時(shí)鳖擒,0x0可表示蒋荚。所以size01都可理解)
image.png

自定義:

  • 占用內(nèi)存空間最大case大小 + enum自身大小:
    image.png

    如果不清楚Foo2case大小size為何為18圆裕,可查看內(nèi)存對齊

順帶提供一個(gè)struct(5個(gè)屬性值)大小計(jì)算方式

MyStruct1 內(nèi)存計(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ù)使用:
    image.png
  • 直接使用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é)
image.png
  • 輸出SIL中間代碼,可以看到是使用alloc_box創(chuàng)建枚舉值吠撮,內(nèi)部調(diào)用了swift_allocObject,去申請空間:
    image.png

SIL官方文檔中泥兰,有介紹alloc_box:

image.png

匯編層也可佐證

image.png

6.OC橋接

  • OC枚舉僅支持Int類型,而swift支持多種類型迈嘹。

6.1 OC使用swift枚舉:

    1. swift中創(chuàng)建Int類型枚舉值秀仲,使用@objc聲明
      image.png
    1. @objc聲明后神僵,橋接文件中保礼,自動生成了OCSWIFT_ENUM:
      image.png
    1. OC文件中炮障,導(dǎo)入swift橋接頭文件铝阐,直接調(diào)用SwiftEnum
      image.png

6.2 swift使用OC枚舉:

  1. OC.h頭文件聲明枚舉類型:
  • typedef NS_ENUM(NSUInteger, OCEnum):自動轉(zhuǎn)換成swift枚舉
  • typedef enum:轉(zhuǎn)換成結(jié)構(gòu)體
    image.png
  1. 橋接文件中,添加OC頭文件:

    image.png

  2. 自動生成swift文件中吹害,可以看到轉(zhuǎn)換的類型:

    image.png

    image.png

  3. swift文件中使用:

  • NS_ENUM生成:可正常使用
  • typedef enum生成:只能通過通過值初始化,再使用
    image.png

6.3 OC使用swift枚舉:

  • 如果swift不是Int類型纵穿,而又希望OC能用谓媒,只能自己做個(gè)橋接句惯。

(例如: 原本swift枚舉類型String抢野,可直接通過rawValue讀取值指孤。
為了兼容OC,把類型改成Int逝嚎,自定義計(jì)算型屬性补君,禁止使用默認(rèn)的rawValue讀让粱ァ)

  • swift中創(chuàng)建int類型枚舉敞掘,自定義string計(jì)算屬性玖雁,并禁止rawValue的使用赫冬。

    image.png

  • OC直接使用

    image.png

  • 如果還希望OC能訪問到swift對應(yīng)的String值:

使用class的類方法兼容

  • class需要繼承NSObject膛薛,類函數(shù)完成枚舉String的映射补鼻。enum禁止rawValue雅任,改用string計(jì)算屬性獲取

    image.png

  • 橋接文件中可以看到生成了OC類方法枚舉:

    image.png

    image.png

  • OC文件中使用:

    image.png

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

image.png

7.2 rawValue的讀取

  • SIL文件中沪么,搜索rawValuegetter方法:
    switch跳轉(zhuǎn)指定case,執(zhí)行函數(shù),得到case內(nèi)容锌半,返回case內(nèi)容
    image.png

有2個(gè)疑問:

  1. 默認(rèn)屬性(字符串)是什么時(shí)候創(chuàng)建的?
  2. 如何記錄case名對應(yīng)值的拳喻?
  1. 默認(rèn)屬性(字符串)是什么時(shí)候創(chuàng)建的哭当?

編譯期就會生成所有符號

image.png

  • 所以上面rawValue讀取時(shí),可直接通過string_literalMachO讀取字符冗澈。
  1. 如何記錄case名對應(yīng)值的钦勘?
  • String類型枚舉caserawValue值(打印結(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:):

    image.png

    完整流程

  • 1.創(chuàng)建:
    創(chuàng)建枚舉(格式:元組(Array<T>,Pointer)猎物,此例中TString) ,再遍歷創(chuàng)建所有枚舉項(xiàng)角塑。

  • 2.查詢:
    通過_findStringSwitchCase 獲取入?yún)⒅?/code>的index蔫磨,使用switch通過index(int類型)匹配到case,匹配成功:返回optional的some值圃伶,匹配失敗堤如,直接返回nil

  • swift源碼中搜索findStringSwitchCase,可以看到是通過for遍歷匹配index

    image.png


enum枚舉較為簡單窒朋,我們了解了與其他語言差異搀罢、用法,順帶探索大小源碼實(shí)現(xiàn)炼邀。

  • 下一節(jié)魄揉,介紹swift閉包
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拭宁,一起剝皮案震驚了整個(gè)濱河市洛退,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杰标,老刑警劉巖兵怯,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異腔剂,居然都是意外死亡媒区,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門掸犬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來袜漩,“玉大人,你說我怎么就攤上這事湾碎≈婀ィ” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵介褥,是天一觀的道長座掘。 經(jīng)常有香客問我,道長柔滔,這世上最難降的妖魔是什么溢陪? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮睛廊,結(jié)果婚禮上形真,老公的妹妹穿的比我還像新娘。我一直安慰自己超全,他們只是感情好咆霜,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著卵迂,像睡著了一般裕便。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上见咒,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天偿衰,我揣著相機(jī)與錄音,去河邊找鬼改览。 笑死下翎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宝当。 我是一名探鬼主播视事,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼庆揩!你這毒婦竟也來了俐东?” 一聲冷哼從身側(cè)響起跌穗,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎虏辫,沒想到半個(gè)月后蚌吸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡砌庄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年羹唠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娄昆。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡佩微,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出萌焰,到底是詐尸還是另有隱情哺眯,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布杆怕,位于F島的核電站族购,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏陵珍。R本人自食惡果不足惜寝杖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望互纯。 院中可真熱鬧瑟幕,春花似錦、人聲如沸留潦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽兔院。三九已至殖卑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間坊萝,已是汗流浹背孵稽。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留十偶,地道東北人菩鲜。 一個(gè)月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像惦积,于是被迫代替她去往敵國和親接校。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容

  • C語言枚舉 一周七天可以寫成 第?個(gè)枚舉成員的默認(rèn)值為整型的 0狮崩,后?的枚舉值依次類推蛛勉,如果我們想更改鹿寻,只需要這樣...
    Mjs閱讀 228評論 0 1
  • SwiftDay011.MySwiftimport UIKitprintln("Hello Swift!")var...
    smile麗語閱讀 3,829評論 0 6
  • 枚舉入門 基本定義 枚舉作為 Swift 的 一等類型, 我們正好可以通過枚舉董习, 來 一一列舉 Swift 其它 ...
    overla5閱讀 281評論 0 2
  • 基本使用 1烈和、從語法上看爱只,對比我們熟悉的其他語言皿淋,每個(gè)成員前面多了一個(gè) case關(guān)鍵字。2恬试、并且跟我們熟悉的另一點(diǎn)...
    swift加oc閱讀 1,794評論 0 1
  • 首先感覺Swift的枚舉確實(shí)相對OC來說功能和語法都擴(kuò)展很多窝趣。首先說一下枚舉的定義。 枚舉聲明的類型是囊括可能狀態(tài)...
    正直走閱讀 294評論 0 0