本篇文章我们一起来探索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 的值获取方式,枚举的初始化,及其模式匹配。