Swift枚举详解

439 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情

一、Enum

枚举的基本用法

Swift语言中使用**enum**关键字来进行枚举的创建。

///创建一个姓氏枚举类型
enum Surname {
    casecasecasecase 李
}

上面的代码创建了一个姓氏枚举类型,这个枚举类型中定义了4个枚举值,分别是赵、钱、孙、李,上面的写法将4个枚举值分别在4个case语句中定义,开发者也可以在1个case子句中完成多个枚举值的定义,如

///创建一个姓氏枚举类型
enum Surname {
    case 赵, 钱,孙,李
}

枚举的原始值和相关值

枚举的原始值特性可以将枚举值与另一种数据类型进行绑定,相关值则可以为枚举值关联一些其他数据。通过相关值,开发者可以实现复杂的枚举类型

枚举的原始值

swift语言中的枚举支持开发者声明一个原始值类型,并将某个已经存在的类型的值与枚举值进行绑定,枚举指定原始值类型的语法与继承的语法有些类似,示例代码如下:

///为枚举类型指定一个原始值类型
enum CharEnum: Character {
    ///通过赋值的方式为枚举值设置一个原始值
    case a = "a"
    case b = "b"
    case c = "c"
    case d = "d"
}

如果开发者要指定枚举的原始值类型为Int类型,那么可以只设置第一个枚举值的原始值,其后的枚举值的原始值会在第一个枚举值原始值的基础上依次递增,示例如下:

enum IntEnum: Int {
    ///第一个枚举值的原始值设置为1
    case a = 1
    ///默认原始值为2
    case b
    ///默认原始值为3
    case c
    ///默认原始值为4
    case d
}

通过枚举类型中的**rawValue**属性来获取枚举的原始值,示例如下:

enum CharEnum: Character {
    ///通过赋值的方式为枚举值设置一个原始值
    case a = "a"
    case b = "b"
    case c = "c"
    case d = "d"
}

var char = CharEnum.a
var value = char.rawValue
print(value)

隐式RawValue分配是建立在swift的类型推断机制上的

enum DayOfWeek: String {
    case mon, tue, wed, thu, fri = "Hello World", sat, sun
}
print(DayOfWeek.mon.rawValue)
print(DayOfWeek.fri.rawValue)
print(DayOfWeek.sat.rawValue)

lldb打印输出

mon
Hello World
sat

我们当前的系统已经默认给我们的每一个枚举值分配了一个字符串,而这个字符串其实跟我们枚举成员值的字符串是一致的。那么它到底是怎么做到的哪,我们在SIL文件看一下。

enum DayOfWeek : String {
  case mon, tue, wed, thu, fri, sat, sun
  init?(rawValue: String)
  typealias RawValue = String
  var rawValue: String { get }
}
// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
  alloc_global @$s4main1xSSvp                     // id: %2
  %3 = global_addr @$s4main1xSSvp : $*String      // user: %8
  %4 = metatype $@thin DayOfWeek.Type
  %5 = enum $DayOfWeek, #DayOfWeek.mon!enumelt    // user: %7
  // function_ref DayOfWeek.rawValue.getter
  %6 = function_ref @$s4main9DayOfWeekO8rawValueSSvg : $@convention(method) (DayOfWeek) -> @owned String // user: %7
  %7 = apply %6(%5) : $@convention(method) (DayOfWeek) -> @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
} 

image.png

这里就是在访问我们rawValuegetter方法。

sil hidden @$s4main9DayOfWeekO8rawValueSSvg : $@convention(method) (DayOfWeek) -> @owned String {
// %0 "self"                                      // users: %2, %1
bb0(%0 : $DayOfWeek):
  debug_value %0 : $DayOfWeek, let, name "self", argno 1 // id: %1
  switch_enum %0 : $DayOfWeek, case #DayOfWeek.mon!enumelt: bb1, case #DayOfWeek.tue!enumelt: bb2, case #DayOfWeek.wed!enumelt: bb3, case #DayOfWeek.thu!enumelt: bb4, case #DayOfWeek.fri!enumelt: bb5, case #DayOfWeek.sat!enumelt: bb6, case #DayOfWeek.sun!enumelt: bb7 // id: %2

switch_enum %0 就是我们传进来的当前枚举成员值mon,接下来就是一个模式匹配,匹配我们当前的成员值。匹配上了**swift case #DayOfWeek.mon!enumelt: bb1**代码块。

bb1:                                              // Preds: bb0
  %3 = string_literal utf8 "mon"                  // user: %8

那么这个字符串 "mon" 是从哪里得到的哪?这样的字符串其实就是一个字符串常量,而字符串常量存储在哪里哪,我们把Mach-o文件拖到MachoView应用来查看一下。

image.png