Swift枚舉

Swift中,枚舉的創(chuàng)建方式如下;

/*寫法1*/

enum LTSeasonOne{

? ? case FIRST

? ? case SECOND

? ? case THIRD

? ? case FORTH

}

/*寫法2*/

enum LTSeasonTwo{

? ? case FIRST,SECOND,THIRD,FORTH

}

如果沒有指定枚舉值的類型,那么enum默認枚舉值是整型的

創(chuàng)建一個枚舉值是String類型的enum(通過指定enum的枚舉值類型來創(chuàng)建)

/*寫法3*/

enum LTSeasonThree: String{

? ? case FIRST = "FIRST",SECOND = "SECOND",THIRD = "THIRD",FORTH = "FORTH"

}

通過查看SIL文件,來探究下具體底層實現(xiàn)邏輯

生成SIL文件,命令:swiftc -emit-sil main.swift > main.sil 其中main.swift是swift文件名

enum LTSeasonTwo {

?? case FIRST,SECOND,THIRD,FORTH

?? @_implements(Equatable, ==(_:_:)) static func __derived_enum_equals(_ a: LTSeasonTwo, _ b: LTSeasonTwo) -> Bool

?? func hash(into hasher: inout Hasher)

?? var hashValue: Int { get }

?}

通過SIL文件可知,默認類型的enum,底層實現(xiàn)邏輯

1、底層默認實現(xiàn)Equatable協(xié)議,并實現(xiàn)__derived_enum_equals方法

2、增加屬性hashValue用于獲取枚舉值的原始值

enum LTSeasonThree : String {

?? case FIRST, SECOND, THIRD, FORTH

?? init?(rawValue: String)

?? typealias RawValue = String

?? var rawValue: String { get }

?}

通過SIL文件可知,指定類型的enum,SIL文件中的enum實現(xiàn)邏輯

1、默認增加了一個可選類型的init方法

2、給枚舉類型,通過typealias取了一個別名:RawValue

3、增加一個屬性rawValue,用于獲取枚舉值的原始值

通過SIL文件來,分析rawValue的get方法:

// LTSeasonThree.rawValue.getter

?sil hidden @$s4main13LTSeasonThreeO8rawValueSSvg : $@convention(method) (LTSeasonThree) -> @owned String {

?// %0 "self"? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // users: %2, %1

?bb0(%0 : $LTSeasonThree):

?? debug_value %0 : $LTSeasonThree, let, name "self", argno 1 // id: %1

?? switch_enum %0 : $LTSeasonThree, case #LTSeasonThree.FIRST!enumelt: bb1, case #LTSeasonThree.SECOND!enumelt: bb2, case #LTSeasonThree.THIRD!enumelt: bb3, case #LTSeasonThree.FORTH!enumelt: bb4 // id: %2

?bb1:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // Preds: bb0

?? %3 = string_literal utf8 "FIRST"? ? ? ? ? ? ? ? // user: %8

?? %4 = integer_literal $Builtin.Word, 5? ? ? ? ? // 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 @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@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 bb5(%8 : $String)? ? ? ? ? ? ? ? ? ? ? ? ? ? // id: %9

?bb2:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // Preds: bb0

?? %10 = string_literal utf8 "SECOND"? ? ? ? ? ? ? // user: %15

?? %11 = integer_literal $Builtin.Word, 6? ? ? ? ? // 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 @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@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 bb5(%15 : $String)? ? ? ? ? ? ? ? ? ? ? ? ? // id: %16

?bb3:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // Preds: bb0

?? %17 = string_literal utf8 "THIRD"? ? ? ? ? ? ? // user: %22

?? %18 = integer_literal $Builtin.Word, 5? ? ? ? ? // 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 @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@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 bb5(%22 : $String)? ? ? ? ? ? ? ? ? ? ? ? ? // id: %23

?bb4:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // Preds: bb0

?? %24 = string_literal utf8 "FORTH"? ? ? ? ? ? ? // user: %29

?? %25 = integer_literal $Builtin.Word, 5? ? ? ? ? // 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 @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@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 bb5(%29 : $String)? ? ? ? ? ? ? ? ? ? ? ? ? // id: %30

?// %31? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // user: %32

?bb5(%31 : $String):? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // Preds: bb4 bb3 bb2 bb1

?? return %31 : $String? ? ? ? ? ? ? ? ? ? ? ? ? ? // id: %32

?} // end sil function '$s4main13LTSeasonThreeO8rawValueSSvg'

通過上述SIL文件可知,rawVa lue的get方法,主要有以下幾步:

1、接收一個枚舉值,用于匹配對應的分支?bb0(%0 : $LTSeasonThree):

2、在對應分支創(chuàng)建對應的String ?switch_enum %0 : $LTSeasonThree, case #LTSeasonThree.FIRST!enumelt: bb1, case #LTSeasonThree.SECOND!enumelt: bb2, case #LTSeasonThree.THIRD!enumelt: bb3, case #LTSeasonThree.FORTH!enumelt: bb4 // id: %2

3、返回對應的String ?return %31 : $String? ? ? ? ? ? ? ? ? ? ? ? ? ? // id: %32

了解了枚舉的底層原理之后,繼續(xù)探索枚舉的init調(diào)用時機

1、定義一個符號斷點

符號斷點

2、開啟Debug模式

開啟Debug

使用如下代碼調(diào)用:

print(LTSeasonThree.FIRST)

print(LTSeasonThree.FIRST.rawValue)

運行后發(fā)現(xiàn)init方法都不會進入

通過如下方式調(diào)用:

print(LTSeasonThree.init(rawValue: "FIRST")!)

print(LTSeasonThree(rawValue: "FIRST")!)

發(fā)現(xiàn)init方法可以進入

得出如下結(jié)論:enum中init方法的調(diào)用是通過 .init(rawValue:) 或 (rawValue)觸發(fā)的

init方法底層分析

init方法

從上圖可知:(593行開始)

調(diào)用方法創(chuàng)建一個數(shù)組,并返回一個元祖(tuple)類型

元祖第一個存放元素的值(595行)

元祖第二個存放當前的指針(596行)

創(chuàng)建字符串的引用 (598行)

當前字符串的長度為5,即在內(nèi)存中分配的大小 (599行)

init方法

調(diào)用了一個方法:_findStringSwitchCase(cases:string:)去匹配

取出當前index (660行)

比較當前的返回值于index。(661行)

根據(jù)條件進行分支調(diào)整,如果成功跳轉(zhuǎn)到bb9,如果不成功跳轉(zhuǎn)到bb10 (662行)

bb9 可知 成功則返回當前的enum (見下圖bb17)

bb10可知,不成功則繼續(xù)匹配(見下圖:bb11,bb12)

init方法
init方法

如果全部沒有匹配,那么構(gòu)建一個.none類型的Optional 表示nil (見bb16,第713行)

如果匹配成功,則構(gòu)建一個.some類型的Optional,表示有值(見bb17,第718行)

所以當enum匹配不上時,會返回nil的原因

總結(jié):

使用rawValue的本質(zhì)是調(diào)用get方法(get方法中的String在編譯時期就已經(jīng)存儲好了,即存放在Mach-O文件的__Text.cstring中,且是連續(xù)的內(nèi)存空間)

rawValue的get方法中的分支構(gòu)建的字符串,主要是從Mach-O文件對應地址取出的字符串,然后在返回

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift 枚舉(enum)詳解 [TOC] 本文將介紹Swift中枚舉的一些用法和其底層原理的一些探索,以及探索...
    just東東閱讀 16,912評論 6 22
  • 前言 本篇文章將講述Swift中很常用的也很重要的一個知識點 ?? Enum枚舉。首先會介紹與OC中枚舉的差別,接著...
    深圳_你要的昵稱閱讀 2,689評論 0 5
  • C語言枚舉 一周七天可以寫成 第?個枚舉成員的默認值為整型的 0,后?的枚舉值依次類推,如果我們想更改,只需要這樣...
    Mjs閱讀 360評論 0 1
  • C語言的枚舉 C語言的枚舉寫法 我們通過枚舉表示一周的七天 c語言中,枚舉的第一個成員默認是為0,后面的枚舉值一次...
    浪的出名閱讀 514評論 0 1
  • 1. 枚舉, 使用enum來創(chuàng)建枚舉, 類似于類的命名類型, 枚舉類型賦值可以是字符串/字符/整形/浮點型, 枚舉...
    chrisdev閱讀 210評論 0 0

友情鏈接更多精彩內(nèi)容