C语言枚举
enum 枚举名 {
枚举值1,
枚举值2,
……};
一周七天可以写成
enum week
{
MON, TUE, WED, THU, FRI, SAT, SUN
};
第⼀个枚举成员的默认值为整型的 0,后⾯的枚举值依次类推,如果我们想更改,只需要这样操作
enum week
{
MON = 1, TUE, WED, THU, FRI, SAT, SUN
};
那如何定义⼀个枚举变量
enum week
{
MON = 1, TUE, WED, THU, FRI, SAT, SUN
} week;
//可以省略声明的枚举
enum week
{
MON = 1, TUE, WED, THU, FRI, SAT, SUN
} week;
Swift 枚举写法
enum week{
case MONDAY
case TUEDAY
case WEDDAY
case THUDAY
case FRIDAY
case SATDAY
case SUNDAY
}
上述代码也可以直接⼀个 case ,然后⽤逗号隔开
enum week{
case MONDAY, TUEDAY, WEDDAY, THUDAY, FRIDAY, SATDAY, SUNDAY
}
枚举值默认是整形,也以表达为String
enum week: String
{
case MON = "MON"
case TUE = "TUE"
case WED = "WED"
case THU = "THU"
case FRI = "FRI"
case SAT = "SAT"
case SUN = "SUN"
}
我们赋值的字符串叫做原始值(RawValue)
,,如果我们不想写后⾯的字符串,这个时候我们就 可以使⽤ 隐⼠ RawValue 分配
enum week: Int { case mon, tue, wed, thu, fri = 10, sat, sun }
print(week.fri.rawValue)
···········
10
enum week: String { case MON, TUE, WED, THU, FRI , SAT, SUN }
print(week.FRI.rawValue)
···········
FRI
当改成String型之后,打印的值就变成了当前枚举值了。
通过swiftc -emit-sil main.swift | xcrun swift-demangle > ./main.sil && open main.sil
查看sil
代码
enum week : String {
case MON, TUE, WED, THU, FRI, SAT, SUN
typealias RawValue = String
init?(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 @main.w : Swift.String // id: %2
%3 = global_addr @main.w : Swift.String : $*String // user: %8
%4 = metatype $@thin week.Type
%5 = enum $week, #week.MON!enumelt // user: %7
// function_ref week.rawValue.getter
%6 = function_ref @main.week.rawValue.getter : Swift.String : $@convention(method) (week) -> @owned String // user: %7
%7 = apply %6(%5) : $@convention(method) (week) -> @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
} // end sil function 'main'
%6调用了get方法,7%将%5也就是枚举值MON传参进去
// week.rawValue.getter
sil hidden @main.week.rawValue.getter : Swift.String : $@convention(method) (week) -> @owned String {
// %0 // users: %2, %1
bb0(%0 : $week):
//声明一个变量self,等于参数week
debug_value %0 : $week, let, name "self", argno 1 // id: %1
//匹配枚举值,跳转到对应的分支
switch_enum %0 : $week, case #week.MON!enumelt: bb1, case #week.TUE!enumelt: bb2, case #week.WED!enumelt: bb3, case #week.THU!enumelt: bb4, case #week.FRI!enumelt: bb5, case #week.SAT!enumelt: bb6, case #week.SUN!enumelt: bb7 // id: %2
//创建String,跳转bb8
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 bb8(%8 : $String) // id: %9
...
//返回
// %52 // user: %53
bb8(%52 : $String): // Preds: bb7 bb6 bb5 bb4 bb3 bb2 bb1
return %52 : $String // id: %53
} // end sil function 'main.week.rawValue.getter : Swift.String'
case 和 rawValue
print(week.MON)
print(week.MON.rawValue)
虽然打印的都是MON
,但是一个是枚举类型,一个是字符串类型
枚举的init
通过sil文件查看init方法
// week.init(rawValue:)
sil hidden @main.week.init(rawValue: Swift.String) -> main.week? : $@convention(method) (@owned String, @thin week.Type) -> Optional<week> {
// %0 // users: %164, %158, %79, %3
bb0(%0 : $String, %1 : $@thin week.Type):
%2 = alloc_stack $week, var, name "self" // users: %162, %154, %143, %132, %121, %110, %99, %88, %165, %159
debug_value %0 : $String, let, name "rawValue", argno 1 // id: %3
%4 = integer_literal $Builtin.Word, 7 // 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: %80, %79
%8 = tuple_extract %6 : $(Array<StaticString>, Builtin.RawPointer), 1 // user: %9
%9 = pointer_to_address %8 : $Builtin.RawPointer to [strict] $*StaticString // users: %17, %69, %59, %49, %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 // id: %13
bb1: // Preds: bb0
%14 = integer_literal $Builtin.Int8, 2 // user: %16
br bb2 // id: %15
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 // id: %23
//通过方法来进行匹配
bb14: // Preds: bb13
%76 = struct $StaticString (%72 : $Builtin.Word, %71 : $Builtin.Word, %74 : $Builtin.Int8) // user: %77
store %76 to %69 : $*StaticString // id: %77
// function_ref _findStringSwitchCase(cases:string:)
%78 = function_ref @Swift._findStringSwitchCase(cases: [Swift.StaticString], string: Swift.String) -> Swift.Int : $@convention(thin) (@guaranteed Array<StaticString>, @guaranteed String) -> Int // user: %79
%79 = apply %78(%7, %0) : $@convention(thin) (@guaranteed Array<StaticString>, @guaranteed String) -> Int // users: %149, %138, %127, %116, %105, %94, %83, %147, %136, %125, %114, %103, %92, %81
release_value %7 : $Array<StaticString> // id: %80
debug_value %79 : $Int, let, name "$match" // id: %81
%82 = integer_literal $Builtin.Int64, 0 // user: %84
%83 = struct_extract %79 : $Int, #Int._value // user: %84
%84 = builtin "cmp_eq_Int64"(%82 : $Builtin.Int64, %83 : $Builtin.Int64) : $Builtin.Int1 // user: %85
cond_br %84, bb15, bb16
//如果成功,返回
bb15: // Preds: bb14
%86 = metatype $@thin week.Type
%87 = enum $week, #week.MON!enumelt // user: %89
%88 = begin_access [modify] [static] %2 : $*week // users: %89, %90
store %87 to %88 : $*week // id: %89
end_access %88 : $*week // id: %90
br bb29 // id: %91
//不成功继续匹配
bb16: // Preds: bb14
debug_value %79 : $Int, let, name "$match" // id: %92
%93 = integer_literal $Builtin.Int64, 1 // user: %95
%94 = struct_extract %79 : $Int, #Int._value // user: %95
%95 = builtin "cmp_eq_Int64"(%93 : $Builtin.Int64, %94 : $Builtin.Int64) : $Builtin.Int1 // user: %96
cond_br %95, bb17, bb18 // id: %96
bb29: // Preds: bb27 bb25 bb23 bb21 bb19 bb17 bb15
%162 = load %2 : $*week // user: %163
//返回的是可选类型,如果没有就是nil
%163 = enum $Optional<week>, #Optional.some!enumelt.1, %162 : $week // user: %166
release_value %0 : $String // id: %164
dealloc_stack %2 : $*week // id: %165
br bb30(%163 : $Optional<week>)
// %167 // user: %168
bb30(%167 : $Optional<week>): // Preds: bb29 bb28
return %167 : $Optional<week> // id: %168
} // end sil function 'main.week.init(rawValue: Swift.String) -> main.week?'
枚举的遍历
enum week: String { case MON, TUE, WED, THU, FRI , SAT, SUN }
extension week: CaseIterable{}
var allCase = week.allCases
for c in allCase {
print(c)
}
只要实现了CaseIterable
协议就可以。
关联值
如果我们想⽤枚举表达更复杂的信息,⽽不仅仅是⼀个 RawValue
这么简单,这个时候我们就可以使⽤ Associated Value
,当声明了关联值后,就无法使用init
和RawValue
enum Shape{
case circle(radious: Double)
case rectangle(width: Int, height: Int)
}
var circle = Shape.circle(radious: 10.0)
circle = Shape.circle(radious: 20.0)
switch circle{
case let .circle(radious):
print(radious)
case .rectangle(let width, var height):
height += 10
print(width,height)
}
//也可以单独取出来用
if case let Shape.circle(radious) = circle{
print(radious)
}
当我们只关注不同枚举值的相同关联值
enum Shape{
case rectangle(width: Int, height: Int)
case squar(width1: Int, height1: Int)
}
var circle = Shape.rectangle(width: 30, height: 40)
switch circle{
case let .rectangle(10, x), let .squar(width1: 10, height1: x):
print(x)
default:
print("nil")
}
注意,前后变量要一一对应。不关注的也以用_
来省略
枚举的嵌套
枚举中含有枚举
enum CombineDirect{
enum BaseDirect{
case up
case down
case left
case right
}
case leftUp(combineElement1: BaseDirect, combineElement2: BaseDirect)
case rightUp(combineElement1: BaseDirect, combineElement2: BaseDirect)
case leftDown(combineElement1: BaseDirect, combineElement2: BaseDirect)
case rightDown(combineElement1: BaseDirect, combineElement2: BaseDirect)
}
let combind = CombineDirect.leftDown(combineElement1: .left, combineElement2: .down)
结构体中包含枚举
struct Skill{
enum KeyType{
case up
case down
case left
case right
}
let key: KeyType
func launchSkill(){
switch key {
case .left,.right:
print("left, right")
case .down,.up:
print("up, down")
}
}
}
枚举中包含属性
enum
中能包含计算属性,类型属性。不能包含存储属性
enum Shape{
case circle(radious: Double)
case rectangle(width: Int, height: Int)
// var radious: Double
static var height = 20.0
var width: Double{
get{
return 10.0
}
}
}
枚举中包含方法
enum week: Int {
case MON, TUE, WED, THU, FRI , SAT, SUN
mutating func nextDay(){
if self == .SUN{
self = week(rawValue: 0)!
}else{
self = week(rawValue: self.rawValue + 1)!
}
}
}
枚举的大小
枚举的大小取决于枚举值,默认是UInt8大小,也就是一字节,超过UInt8容量就会升级为UInt16,也就是二字节,以此类推。
存在关联值,取决最大case的大小
enum Shape{
case circle(radious: Double)
case rectangle(width: Int)
}
print(MemoryLayout<Shape>.stride)
print(MemoryLayout<Shape>.size)
·····
16
9
Double占8字节,加上case的1字节,就是9字节,根据字节对齐,所以步长是16。
indirect
enum List<T>{
case end
indirect case node(T, next: List<T>)
}
print(MemoryLayout<List<String>>.size)
print(MemoryLayout<List<String>>.stride)
·············
8
8
打印添加了indirect
的枚举大小为8字节
var node = List<Int>.node(10, next: List<Int>.end)
·················
(lldb) p withUnsafePointer(to: &node, {$0})
(UnsafePointer<swiftTest.List<Int>>) $R6 = 0x0000000100003068
(lldb) x/4g 0x0000000100003068
0x100003068: 0x0000000100419fd0 0x00007fff98c81218
0x100003078: 0x00007fff98c81218 0x00000001007824d0
(lldb) x/4g 0x0000000100419fd0
0x100419fd0: 0x0000000100002030 0x0000000000000002
0x100419fe0: 0x000000000000000a 0x0000000000000000
通过打印地址,发现把声明了indirect
关键字的值放在了堆空间上。
查看sil
// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
alloc_global @main.node : main.List<Swift.Int> // id: %2
%3 = global_addr @main.node : main.List<Swift.Int> : $*List<Int> // user: %16
%4 = metatype $@thin List<Int>.Type
%5 = integer_literal $Builtin.Int64, 10 // user: %6
%6 = struct $Int (%5 : $Builtin.Int64) // user: %13
%7 = metatype $@thin List<Int>.Type
%8 = enum $List<Int>, #List.end!enumelt // user: %14
%9 = alloc_box $<τ_0_0> { var (τ_0_0, next: List<τ_0_0>) } <Int> // users: %15, %10
%10 = project_box %9 : $<τ_0_0> { var (τ_0_0, next: List<τ_0_0>) } <Int>, 0 // users: %12, %11
%11 = tuple_element_addr %10 : $*(Int, next: List<Int>), 0 // user: %13
%12 = tuple_element_addr %10 : $*(Int, next: List<Int>), 1 // user: %14
store %6 to %11 : $*Int // id: %13
store %8 to %12 : $*List<Int> // id: %14
%15 = enum $List<Int>, #List.node!enumelt.1, %9 : $<τ_0_0> { var (τ_0_0, next: List<τ_0_0>) } <Int> // user: %16
store %15 to %3 : $*List<Int> // id: %16
%17 = integer_literal $Builtin.Int32, 0 // user: %18
%18 = struct $Int32 (%17 : $Builtin.Int32) // user: %19
return %18 : $Int32 // id: %19
} // end sil function 'main'
通过官方文档可以查看alloc_box的解释
Allocates a reference-counted @box on the heap large enough to hold a value of type T, along with a retain count and any other metadata required by the runtime. The result of the instruction is the reference-counted @box reference that owns the box. The project_box instruction is used to retrieve the address of the value inside the box.
在堆上分配一个引用计数的@box,其大小足以容纳类型T的值,以及retain计数和运行时所需的任何其他元数据。该指令的结果是引用计数的@box引用,该引用拥有该框。project_box指令用于检索框内值的地址。
OC-Swift混编
我们在Swift的枚举前加上@objc
就可以了
@objc enum WEEK: Int {
case MON, TUE
}
编译一下,我们就可以在.h文件中查看
typedef SWIFT_ENUM(NSInteger, WEEK, closed) {
WEEKMON = 0,
WEEKTUE = 1,
};
OC仅能使用声明为Int
类型的。
NS_ENUM
**********OC**********
NS_ENUM(NSInteger, OCENUM){
Value1,
Value2
};
**********SWIFT**********
public var OCENUM: OCENUM
public enum OCENUM : Int {
case Value1 = 0
case Value2 = 1
}
typedef enum
如果用typedef enum
来声明,就会编译成结构体
**********OC**********
typedef enum {
Num1,
Num2
} OCNum;
**********SWIFT**********
public struct OCNum : Equatable, RawRepresentable {
public init(_ rawValue: UInt32)
public init(rawValue: UInt32)
public var rawValue: UInt32
}
typedef NS_ENUM
**********OC**********
typedef NS_ENUM(NSInteger, CEnum) {
CEnumInvalid = 0,
CEnumA = 1,
CEnumB,
CEnumC
};
**********SWIFT**********
public enum CEnum : Int {
case invalid = 0
case A = 1
case B = 2
case C = 3
}