Swift枚举

Swift中,枚举的创建方式如下;

/*写法1*/

enum LTSeasonOne{

    case FIRST

    case SECOND

    case THIRD

    case FORTH

}

/*写法2*/

enum LTSeasonTwo{

    case FIRST,SECOND,THIRD,FORTH

}

如果没有指定枚举值的类型,那么enum默认枚举值是整型的

创建一个枚举值是String类型的enum(通过指定enum的枚举值类型来创建)

/*写法3*/

enum LTSeasonThree: String{

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

}

通过查看SIL文件,来探究下具体底层实现逻辑

生成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,底层实现逻辑

1、底层默认实现Equatable协议,并实现__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实现逻辑

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、在对应分支创建对应的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

了解了枚举的底层原理之后,继续探索枚举的init调用时机

1、定义一个符号断点

符号断点

2、开启Debug模式

开启Debug

使用如下代码调用:

print(LTSeasonThree.FIRST)

print(LTSeasonThree.FIRST.rawValue)

运行后发现init方法都不会进入

通过如下方式调用:

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

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

发现init方法可以进入

得出如下结论:enum中init方法的调用是通过 .init(rawValue:) 或 (rawValue)触发的

init方法底层分析

init方法

从上图可知:(593行开始)

调用方法创建一个数组,并返回一个元祖(tuple)类型

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

元祖第二个存放当前的指针(596行)

创建字符串的引用 (598行)

当前字符串的长度为5,即在内存中分配的大小 (599行)

init方法

调用了一个方法:_findStringSwitchCase(cases:string:)去匹配

取出当前index (660行)

比较当前的返回值于index。(661行)

根据条件进行分支调整,如果成功跳转到bb9,如果不成功跳转到bb10 (662行)

bb9 可知 成功则返回当前的enum (见下图bb17)

bb10可知,不成功则继续匹配(见下图:bb11,bb12)

init方法
init方法

如果全部没有匹配,那么构建一个.none类型的Optional 表示nil (见bb16,第713行)

如果匹配成功,则构建一个.some类型的Optional,表示有值(见bb17,第718行)

所以当enum匹配不上时,会返回nil的原因

总结:

使用rawValue的本质是调用get方法(get方法中的String在编译时期就已经存储好了,即存放在Mach-O文件的__Text.cstring中,且是连续的内存空间)

rawValue的get方法中的分支构建的字符串,主要是从Mach-O文件对应地址取出的字符串,然后在返回

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • Swift 枚举(enum)详解 [TOC] 本文将介绍Swift中枚举的一些用法和其底层原理的一些探索,以及探索...
    just东东阅读 16,338评论 6 22
  • 前言 本篇文章将讲述Swift中很常用的也很重要的一个知识点 👉 Enum枚举。首先会介绍与OC中枚举的差别,接着...
    深圳_你要的昵称阅读 2,471评论 0 5
  • C语言枚举 一周七天可以写成 第⼀个枚举成员的默认值为整型的 0,后⾯的枚举值依次类推,如果我们想更改,只需要这样...
    Mjs阅读 225评论 0 1
  • C语言的枚举 C语言的枚举写法 我们通过枚举表示一周的七天 c语言中,枚举的第一个成员默认是为0,后面的枚举值一次...
    浪的出名阅读 380评论 0 1
  • 1. 枚举, 使用enum来创建枚举, 类似于类的命名类型, 枚举类型赋值可以是字符串/字符/整形/浮点型, 枚举...
    chrisdev阅读 190评论 0 0