Swift枚举

806 阅读3分钟

本篇文章我们一起来探索swift枚举的实现

原始值(RawValue)

枚举值

我们先定义如下枚举

enum Weekend: String {
    case MON
    case TUE
    case WED
    case THU
}

Weekend是值为 String的枚举,我们输入其RawValue

var w = Weekend.MON.rawValue
print(w) // MON

输出结果和我们预想的差不多,输出一个字符串 Mon。

SIL分析

我们通过SIL来分析,RawValue值为什么是Mon,

swiftc -emit-sil main.swift | xcrun swift-demangle > ./main.sil && open main.sil

sil文件:

enum Weekend : String {
  case MON
  case TUE
  case WED
  case THU
  typealias RawValue = String
  init?(rawValue: String)
  var rawValue: String { get } // 1
}

// Weekend.rawValue.getter
sil hidden @main.Weekend.rawValue.getter : Swift.String : $@convention(method) (Weekend) -> @owned String {
// %0 "self"                                      // users: %2, %1
bb0(%0 : $Weekend):
  debug_value %0 : $Weekend, let, name "self", argno 1 // id: %1
  // 2
  switch_enum %0 : $Weekend, case #Weekend.MON!enumelt: bb1, case #Weekend.TUE!enumelt: bb2, case #Weekend.WED!enumelt: bb3, case #Weekend.THU!enumelt: bb4 // id: %2

bb1:                                              // Preds: bb0
  %3 = string_literal utf8 "MON"                  // user: %8
  %4 = integer_literal $Builtin.Word, 3           // 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 @Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String : $@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)
  • 1,我们可以看到 rawValue是一个getter方法。
  • 2,通过switch_enum,来对 枚举值进行判断。
  • bb1: 在编译器,编译器就生成了 “Mon”字符串,存放在 MachO文件的__text段, 当匹配到是 枚举值是 Mon时,就返回 “Mon”

初始化方法

在本例中我们可以看到,swift枚举通过 init?(rawValue:)方法进行初始化的。我们分别来初始化两个枚举变量

var mon = Weekend.init(rawValue: "MON")
var sat = Weekend.init(rawValue: "SAT")
print(mon) // Optional(LYSwift.Weekend.MON)
print(sat) // nil

为什么 mon 枚举有值,sat枚举值为 nil 呢?,我们来看下其init方法

// 1
sil hidden @main.Weekend.init(rawValue: Swift.String) -> main.Weekend? : $@convention(method) (@owned String, @thin Weekend.Type) -> Optional<Weekend> {
// %0 "rawValue"                                  // users: %101, %95, %49, %3
// %1 "$metatype"
bb0(%0 : $String, %1 : $@thin Weekend.Type):
// 2
  %2 = alloc_stack $Weekend, var, name "self"     // users: %99, %91, %80, %69, %58, %102, %96
  debug_value %0 : $String, let, name "rawValue", argno 1 // id: %3
  %4 = integer_literal $Builtin.Word, 4           // user: %6
  // function_ref _allocateUninitializedArray<A>(_:)
  %5 = function_ref @Swift._allocateUninitializedArray<A>(Builtin.Word) -> ([A], Builtin.RawPointer) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %6
  %6 = apply %5<StaticString>(%4) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // users: %8, %7
  %7 = tuple_extract %6 : $(Array<StaticString>, Builtin.RawPointer), 0 // users: %50, %49
  %8 = tuple_extract %6 : $(Array<StaticString>, Builtin.RawPointer), 1 // user: %9
  %9 = pointer_to_address %8 : $Builtin.RawPointer to [strict] $*StaticString // users: %17, %39, %29, %19
  %10 = string_literal utf8 "MON"                 // user: %12
  %11 = integer_literal $Builtin.Word, 3          // user: %16
  %12 = builtin "ptrtoint_Word"(%10 : $Builtin.RawPointer) : $Builtin.Word // user: %16
  br bb1
bb2:                                              // Preds: bb1
  %16 = struct $StaticString (%12 : $Builtin.Word, %11 : $Builtin.Word, %14 : $Builtin.Int8) // user: %17
  store %16 to %9 : $*StaticString                // id: %17
  %18 = integer_literal $Builtin.Word, 1          // user: %19
  %19 = index_addr %9 : $*StaticString, %18 : $Builtin.Word // user: %27
  %20 = string_literal utf8 "TUE"                 // user: %22
  %21 = integer_literal $Builtin.Word, 3          // user: %26
  %22 = builtin "ptrtoint_Word"(%20 : $Builtin.RawPointer) : $Builtin.Word // user: %26
  br bb3 
  
  
bb8:                                              // Preds: bb7
  %46 = struct $StaticString (%42 : $Builtin.Word, %41 : $Builtin.Word, %44 : $Builtin.Int8) // user: %47
  store %46 to %39 : $*StaticString               // id: %47
  // function_ref _findStringSwitchCase(cases:string:)
  %48 = function_ref @Swift._findStringSwitchCase(cases: [Swift.StaticString], string: Swift.String) -> Swift.Int : $@convention(thin) (@guaranteed Array<StaticString>, @guaranteed String) -> Int // user: %49
  %49 = apply %48(%7, %0) : $@convention(thin) (@guaranteed Array<StaticString>, @guaranteed String) -> Int // users: %86, %75, %64, %53, %84, %73, %62, %51
  release_value %7 : $Array<StaticString>         // id: %50
  debug_value %49 : $Int, let, name "$match"      // id: %51
  %52 = integer_literal $Builtin.Int64, 0         // user: %54
  %53 = struct_extract %49 : $Int, #Int._value    // user: %54
  %54 = builtin "cmp_eq_Int64"(%52 : $Builtin.Int64, %53 : $Builtin.Int64) : $Builtin.Int1 // user: %55
  cond_br %54, bb9, bb10    
  • 1,其返回值为 Optional<Weekend>类型。
  • 2,alloc_stack:它存储在栈空间上。
  • 3,bb0,bb2...:它会将其所有的枚举值的rawValue:MON,TUE,WED,THU,存放到一个数组里面。
  • 4,bb8:通过 _findStringSwitchCase方法,在数组里面查找传入的RawValue,如果找不到就返回 nil。如果找到了,就返回相对应的枚举值。

关联值

enum Shape {
    case circle(radious: Double)
    case rectangle(width:Int, height: Int)
}

如果枚举有了关联值,那该枚举就没有了原始值。

模式匹配

匹配原始值

var mon = Weekend.init(rawValue: "MON")
switch mon {
    case .MON:
        print("MON")
    case .TUE:
        print("TUE")
    default:
        print("other")
}

这里需要注意的是需要匹配所有模式,可以使用 default 来匹配其他所有枚举值。

匹配关联值

匹配多个case

enum Shape {
    case circle(radious: Double)
    case rectangle(width:Int, height: Int)
}

var shape: Shape? = Shape.circle(radious: 10)

switch shape {
    case .circle(let radious):
        print(radious)
    
    case .rectangle(let width, let height):
        print("width:\(width), height: \(height)")
    
    default:
        print("other")
}

匹配一个case

var shape: Shape = Shape.circle(radious: 10)
if case let Shape.circle(radious) = shape {
    print("\(radious)")
}

总结

我们在这里简单的总结了 swift枚举 的 RawValue 的值获取方式,枚举的初始化,及其模式匹配。