In this article we explore the implementation of swift enumeration

Original value (RawValue)

Enumerated values

Let’s start by defining the following enumeration

enum Weekend: String {
    case MON
    case TUE
    case WED
    case THU
}
Copy the code

Weekend is a String enumeration whose RawValue we enter

var w = Weekend.MON.rawValue
print(w) // MON
Copy the code

The output is pretty much what we expect, with the string Mon.

SIL analysis

So let’s use SIL to analyze why RawValue is Mon,

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

Sil file:

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)Copy the code
  • 1, we can see thatrawValueIs agetterMethods.
  • 2, throughswitch_enumTo judge the enumerated values.
  • bb1: At the compiler, the compiler is generated"Mon"String stored in MachO file__textSegment, when matched toWhen the enumeration value is Mon, return "Mon".

Initialization method

In this case we can see that the Swift enumeration is done through init? (rawValue:) method. Let’s initialize the two enumerated variables separately

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

Why does mon enumeration have a value and SAT enumeration is nil? , let’s look at its init method

// 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    
Copy the code
  • 1, whose return value isOptional<Weekend>Type.
  • 2,alloc_stack: It is stored inStack spaceOn.
  • 3,bb0,bb2...: it will take all of its enumerated valuesRawValue: MON, TUE, WED and THUAnd store it in an array.
  • 4,bb8Through:_findStringSwitchCaseMethod that looks in an array for incomingRawValueIf not found, returnnil. If found, the corresponding enumeration value is returned.

The associated values

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

If an enumeration has an associated value, the enumeration has no original value.

Pattern matching

Match the original value

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

Note here that all patterns need to be matched, and you can use default to match all other enumerated values.

Matching correlation value

Matching multiple cases

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")
}
Copy the code

Match a case

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

Copy the code

conclusion

Here we briefly summarize the RawValue of swift enumeration, initialization of enumeration, and pattern matching.