Swift 枚举

前言

本篇文章将讲述Swift中很常用的也很重要的一个知识点 👉 Enum枚举。首先会介绍与OC中枚举的差别,接着会从底层分析Enum的使用场景,包含枚举的嵌套递归与OC的混编的场景,最后分析枚举的内存大小的计算方式,希望大家能够掌握。

一、OC&Swift枚举的区别

1.1 OC中的NS_ENUM

OC中的枚举和C/C++中的枚举基本一样,具有以下特点👇

  • 仅支持Int类型,默认首元素值为0,后续元素值依次+1
  • 中间的元素有赋值,那么以此赋值为准,后续没赋值的元素值依旧依次+1
    枚举使用示例代码👇
typedef NS_ENUM(NSInteger, WEEK) {
    Mon,
    Tue = 10,
    Wed,
    Thu,
    Fri,
    Sat,
    Sun
};
// 调用代码👇
WEEK a = Mon; 
WEEK b = Tue;
NSLog(@"a = %d, b = %d", (int)a, (int)b);

1.2 Swift中的Enum

Swift中的枚举比OC的强大很多!其特点如下👇

  • 格式: 不用逗号分隔,类型需使用case声明
  • 内容:
    1. 支持Int、Double、String基础类型,也有默认枚举值String类型默认枚举值为key的名称Int、Double数值型默认枚举值为0开始+1递增
    2. 支持自定义选项 👉 不指定支持类型,就没有rawValue,但同样支持case枚举,可自定义关联内容

注意:rawValue在后面枚举的访问中会详细的讲解。

示例代码👇

// 写法一
// 不需要逗号隔开
enum Weak1 {
    case MON
    case TUE
    case WED
    case THU
    case FRI
    case SAT
    case SUN
}

// 写法二
// 也可以直接一个case,然后使用逗号隔开
enum Weak2 {
    case MON, TUE, WED, THU, FRI, SAT, SUN
}

// 定义一个枚举变量
var w: Weak1 = .MON

1.2.2 自定义选项类型

如果在声明枚举时不指定类型,那么可给枚举项添加拓展内容(即自定义类型)。switch-case访问时,可取出拓展类型进行相应的操作。例如👇

// 自定义类型的使用
enum Shape {
    case square(width: Double)
    case circle(radius: Double, borderWidth:Double)
}

func printValue(_ v: Shape) {
    // switch区分case(不想每个case处理,可使用default)
    switch v {
    case .square(let width):
        print(width)
    case .circle(let radius, _):
        print(radius)
    }
}

let s = Shape.square(width: 10)
let c = Shape.circle(radius: 20, borderWidth: 1)
printValue(s)
printValue(c)

二、Swift枚举的使用

接下来我们看看Swift枚举的使用,包含一些特殊的场景的情况。

2.1 枚举的访问

说到枚举的访问,就必须得提一个关键字rawValue,使用案例👇

enum Weak: String{
    case MON, TUE, WED, THU, FRI, SAT, SUN
}
var w = Weak.MON.rawValue
print(w)

运行结果👇


注意:如果enum没有声明类型,是没有rawValue属性的👇

现在问题来了 👉 rawValue对应在底层是如何做到读取到MON值的?

rawValue的取值流程

老规矩,找入口,之前我们都是查看SIL,当然这里也不例外👇

swiftc -emit-sil xx.swift | xcrun swift-demangle >> ./xx.sil && vscode xx.sil

先看看枚举Week👇

接着看看main函数的流程👇

最后看看rawValue的getter方法👇

然后看bb8代码段👇

至此,我们现在知道了,rawValue的底层就是调用的getter方法,getter方法中构造了字符串,但是这个字符串的值(例如“MON”)从哪里取出的呢?其实我们能猜出来,应该是在编译期确定了的,所以,我们打开工程的exec可执行文件,查看Mach-O👇

可见,在__TEXT, __cstring的section段,这些字符串在编译期已经存储好了,而且内存地址是连续的。所以,rawValue的getter方法 👉 case分支中构建的字符串,主要是在Mach-O文件中从对应地址取出的字符串,然后再返回给变量w

case值 & rawValue值

现在我们弄清楚了rawValue值的来源,那么又有一个问题:枚举case值和 rawValue值如何区分呢?下面的代码输出打印结果是什么?

//输出 case值
print(Weak.MON)
//输出 rawValue值
print(Weak.MON.rawValue)

虽然输出的都是MON,但其实并不是相同的,why?看下图👇

上图可知,并不能将枚举的case值赋给字符串类型常量w,同时,也不能将字符串"MON"赋给枚举值t

2.2 枚举初始化init

在OC中,枚举没有初始化一说,而在Swift中,枚举是有init初始化方法👇

Weak.init(rawValue:)

接下来我们来看看这个初始化方法在底层的流程,首先添加代码👇,打上断点

print(Weak.init(rawValue: "MON")!)

打开汇编,运行👇

接着我们还是看SIL代码,关于Weak.init(rawValue:)部分👇

其中,上图中涉及的SIL的指令释义👇

指令名称 指令释义
index_addr 获取当前数组中的第n个元素值的地址(即指针),存储到当前地址中
struct_extract 表示在结构体中取出当前的Int值,Int类型在系统中也是结构体
cond_br 表示比较的表达式,即分支条件跳转(类似于三元表达式)

接着来看看这个关键的函数_findStringSwitchCase的源码👇

我们继续看Weak.init的最终处理代码 👉 bb29代码段👇

至此,我们分析完了Weak.init的底层流程,于是修改之前的调用代码(去掉了之前的感叹号!)👇

print(Weak.init(rawValue: "MON"))
print(Weak.init(rawValue: "Hello"))

编译器会爆出警告(返回的结果是可选型),运行结果👇

所以,现在我们就能明白,为什么一个打印的是可选值,一个打印的是nil。

2.3 枚举遍历:CaseIterable协议

CaseIterable协议,有allCases属性,支持遍历所有case,例如👇

// Double类型
enum Week1: Double, CaseIterable {
    case Mon,Tue, Wed, Thu, Fri, Sat, Sun
}
Week1.allCases.forEach { print($0.rawValue)}

// String类型
enum Week2: String, CaseIterable {
    case Mon,Tue, Wed, Thu, Fri, Sat, Sun
}
Week2.allCases.forEach { print($0.rawValue)}

2.4 枚举关联值

关联值就是上面讲过的自定义类型的枚举,它能表示更复杂的信息,与普通类型的枚举不同点在于👇

  1. 没有rawValue
  2. 没有rawValue的getter方法
  3. 没有初始化init方法

例如

// 自定义类型的使用
enum Shape {
    case square(width: Double)
    case circle(radius: Double, borderWidth:Double)
}

查看其SIL代码👇

中间层代码真的什么都没有!😂

2.5 模式匹配

模式匹配就是针对case的匹配,根据枚举类型,分为2种:

  1. 简单类型的枚举的模式匹配
  2. 自定义类型的枚举(关联值)的模式匹配

2.5.1 简单类型

swift中的简单类型enum匹配需要将所有情况都列举,或者使用default表示默认情况,否则会报错

enum Weak: String{
    case MON
    case TUE
    case WED
    case THU
    case FRI
    case SAT
    case SUN
}

var current: Weak?
switch current {
    case .MON:print(Weak.MON.rawValue)
    case .TUE:print(Weak.MON.rawValue)
    case .WED:print(Weak.MON.rawValue)
    default:print("unknow day")
}

如果去掉default,会报错👇

我们看看SIL代码👇

所以运行上面代码,应该匹配的是default分支👇

2.5.2 关联值类型

关联值类型的模式匹配有两种方式👇

  • switch - case 👉 匹配所有case
  • if - case 👉 匹配单个case
switch - case
enum Shape{
    case circle(radius: Double)
    case rectangle(width: Int, height: Int)
}

let修饰case值👇

let shape = Shape.circle(radius: 10.0)
switch shape{
    //相当于将10.0赋值给了声明的radius常量
    case let .circle(radius):
        print("circle radius: \(radius)")
    case let .rectangle(width, height):
        print("rectangle width: \(width) height: \(height)")
}

也可以let var修饰关联值的入参👇

let shape = Shape.circle(radius: 10.0)
switch shape{
    case .circle(let radius):
        print("circle radius: \(radius)")
    case .rectangle(let width, var height):
        height += 1
        print("rectangle width: \(width) height: \(height)")
}

查看SIL层的代码,看看是怎么匹配的👇

if - case
let circle = Shape.circle(radius: 10.0)
if case let Shape.circle(radius) = circle {
    print("circle radius: \(radius)")
}
通用关联值

如果只关心不同case下的某一个关联值,可以将该关联值用同一个入参替换,例如下面例子中的x👇

enum Shape{
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
    case square(width: Double, height: Double)
}
let shape = Shape.circle(radius: 10)
switch shape{
case let .circle(x), let .square(20, x):
    print(x)
default:
    print("未匹配")
    break
}

注意:不能使用多于1个的通用入参,例如下面的y👇

也可以使用通配符 _👇

let shape = Shape.rectangle(width: 10, height:20)
switch shape{
case let .rectangle(_, x), let .square(_, x):
    print("x = \(x)")
default:
    break
}

还可以这么写👇

let shape = Shape.rectangle(width: 10, height:20)
switch shape{
case let .rectangle(x, _), let .square(_, x):
    print("x = \(x)")
default:
    break
}

大家平时在使用枚举时,还是要注意下面2点👇

  1. 枚举使用过程中不关心某一个关联值,可以使用通配符_标识
  2. OC只能调用Swift中Int类型的枚举

2.6 支持计算型属性 & 函数

Swift枚举中还支持计算属性函数,例如👇

enum Direct: Int {
    case up
    case down
    case left
    case right
    
    // 计算型属性
    var description: String{
        switch self {
        case .up:
            return "这是上面"
        default:
            return "这是\(self)"
        }
    }
    
    // 函数
    func printSelf() {
        print(description)
    }
}

Direct.down.printSelf() 

三、枚举嵌套

枚举的嵌套主要有2种场景👇

  1. 枚举嵌套枚举
  2. 结构体嵌套枚举

3.1 enum嵌套enum

我们先来看看枚举嵌套枚举,我们继续改下上面的例子👇

enum CombineDirect{
    //枚举中嵌套的枚举
    enum BaseDirect{
        case up
        case down
        case left
        case right
    }
    //通过内部枚举组合的枚举值
    case leftUp(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case leftDown(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case rightUp(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case rightDown(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
}

如果初始化一个左上方向,代码👇

let leftUp = CombineDirect.leftUp(baseDIrect1: CombineDirect.BaseDirect.left, baseDirect2: CombineDirect.BaseDirect.up)

3.2 struct嵌套enum

接下来就是结构体嵌套枚举了,例如👇

//结构体嵌套枚举
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 .up, .down:
            print("up, down")
        }
    }
}

3.3 枚举的递归(indirect)

还有一种特殊的场景 递归 👉 枚举中case关联内容使用自己的枚举类型。例如👇

enum Binary<T> {
    case empty
    case node(left: Binary, value:T, right:Binary)
}

一个结构,其左右节点的类型也是自己本身,这时编译器会报错👇

报错原因 👉 使用该枚举时,enum的大小需要case来确定,而case的大小又需要使用到enum大小。所以无法计算enmu的大小,于是报错!
安排 👉 根据编译器提示,需要使用关键字indirect,意思就是将该枚举标记位递归,同时也支持标记单个case,所以可以👇

那么问题来了,indirect在底层干了什么呢?

indirect底层原理

我们先来看这个例子👇

enum List<T>{
    case end
    indirect case node(T, next: List<T>)
}

var node = List<Int>.node(10, next: List<Int>.end)

print(MemoryLayout.size(ofValue: node))
print(MemoryLayout.stride(ofValue: node))

size和stride都是8,换成String类型👇

仍然也是8,看来枚举的大小不受其模板类型大小的影响。

lldb分析

我们先lldb看看其内存的分布👇

上图中我们发现,node的metadata对应的地址0x0000000100562660是分配在上的,所以,indirect关键字其实就是通知编译器,需要分配一块堆区的内存空间,用来存放enumcase。此时case为node时,存储的是引用地址0x0000000100562660,而case为end时,则👇

那为何说地址是堆区呢?我们接着看看SIL代码👇

SIL代码中,是通过alloc_box申请的内存,alloc_box底层调用的是swift_allocObject,所以是堆区,我们可以再node打上断点,查看汇编👇

四、##swift和OC混编枚举

接下来我们看看swift和OC的枚举的混编场景。

4.1 OC使用Swift枚举

首先看看OC调用Swift枚举,那么此时枚举必须具备以下2个条件👇

  1. @objc关键字标记enum
  2. 当前enum必须是Int类型
// Swift中定义枚举
@objc enum Weak: Int{
    case MON, TUE, WED, THU, FRI, SAT, SUN
}

// OC使用
- (void)test{
    Weak mon = WeakMON;
}

4.2 Swift使用OC枚举

反过来,就没限制了,OC中的枚举会自动转换成swift中的enum。

// OC定义
NS_ENUM(NSInteger, OCENUM){
    Value1,
    Value2
};

// swift使用
//1、将OC头文件导入桥接文件
#import "OCFile.h"
//2、使用
let ocEnum = OCENUM.Value1
typedef enum
// OC定义
typedef enum {
    Num1,
    Num2
}OCNum;

// swift使用
let ocEnum = OCNum.init(0)
print(ocEnum)

上图可知,通过typedef enum定义的enum,在swift中变成了一个结构体,并遵循了两个协议:EquatableRawRepresentable

typedef NS_ENUM
// OC定义
typedef NS_ENUM(NSInteger, OCNum) {
    Num1,
    Num2
};
// swift使用
let ocEnum = OCNum.init(rawValue: 0)
print(ocEnum!)

那么自动生成的swift中是这样👇

并没有遵循任何协议!

4.3 OC使用Swift中String类型的枚举

这也是一种常见的场景,解决方案👇

  1. swift中的enum尽量声明成Int整型
  2. 然后OC调用时,使用的是Int整型的
  3. enum再声明一个变量/方法,用于返回固定的字符串,给swift中使用
    示例👇
@objc enum Weak: Int{
    case MON, TUE, WED
    
    var val: String?{
        switch self {
        case .MON:
            return "MON"
        case .TUE:
            return "TUE"
        case .WED:
            return "WED"
        default:
            return nil
        }
    }
}

// OC中使用
Weak mon = WeakMON;

// swift中使用
let weak = Weak.MON.val

五、枚举的大小

主要分析以下几种情况👇

  1. 普通enum
  2. 具有关联值的enum
  3. enum嵌套enum
  4. struct嵌套enum

枚举的大小也是面试中经常问到的问题,重点在于两个函数的区别👇

size: 实际占用内存大小
stride:系统分配的内存大小

5.1 普通enum

最普通的情况,即非嵌套,非自定义类型的枚举,例如👇

enum Weak {
    case MON
}

print(MemoryLayout<Weak>.size)
print(MemoryLayout<Weak>.stride)

再添加一个case,运行👇

继续增加多个case,运行👇

以上可以看出,当case个数为1时,枚举size为0,个数>=2时,size的大小始终是1,why?下面我们来分析分析👇

上图打断点,读取内存可以看出,case都是1字节大小,1个字节是8个byte,按照二进制转换成十进制,那么有255种排列组合(0x00000000 - 0x11111111),所以当case为1个的时候,size的大小是0(二进制是0x0),case数<=255时,size都是1。而超过255个时,会自动扩容sizestride都会增加

5.2 具有关联值的enum

如果是自定义类型的枚举,即关联值类型,size和stride的值会发生什么变化呢?看下面的例子👇

enum Shape{
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
}
print(MemoryLayout<Shape>.size)
print(MemoryLayout<Shape>.stride)

看来关联值的枚举大小和关联值入参有关系,👇

5.3 enum嵌套enum

枚举嵌套枚举,是一种特殊的情况,下面示例大小是多少?👇

enum CombineDirect{
    enum BaseDirect{
        case up, down, left, right
    }
    
    case leftUp(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case rightUp(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case leftDown(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case rightDown(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
}

print(MemoryLayout<CombineDirect>.size)
print(MemoryLayout<CombineDirect>.stride)

从结果中可以看出,enum嵌套enum,和具有关联值的enum的情况是一样的,同样取决于关联值的大小,其内存大小是最大关联值的大小
接着我们看看具体的分布,可以先定义一个变量👇

var combine = CombineDirect.leftDown(baseDirect1: .left, baseDirect2: .down)

lldb查看其内存分布👇

将第一个入参left 改为 up👇

所以,02表示是caseleftDown的关联值的第一个入参的枚举值,那第2个入参是.down,按照规律来算应该是01,但却是81,why?接下来我们看看81代表的是什么值?

  • 在enum CombineDirect中多加4个case

结果是c1

  • 减少一个case

减少一个case项后,是a1

  • 再减少一个case

再减少一个是81,说明81中的8

  • 继续减少,保证case只有2个

结果页是81

  • 添加保证case>10个

如果leftDown 的case索引值大于8,例如上图,leftDown是第1个case,结果e1中的e就是15(十进制),即case选项的索引值。

  • 再减少,保证case leftDown在第9个

果然,上图中leftDown的case索引值是9,打印出来的91中的第一位也是9

综上, 81中的第一位8这个值,有以下几种情况区分👇

  1. 当嵌套enum的case只有2个时,case在内存中的存储是0、8
  2. 当嵌套enum的case大于2,小于等于4时,case在内存中的存储是0、4、8、12
  3. 当嵌套enum的case大于4时,case在内存中的存储是从0、1、2...类推

81中的1,代表什么意思呢?我们改变下关联值入参👇

所以,leftDown减少一个入参,结果是80,加一个入参,结果是01 80,继续再加一个入参👇

关联值的入参是up,down,right,right,对应的枚举值是0,1,3,3,所以可以得出结论👇

  1. enum嵌套enum同样取决于最大case的关联值大小
  2. case中关联值的内存分布,又是根据入参的个数大小来分布的
    2.1 每个入参占一个字节大小的空间(即2个byte位),第2位byte里面存储的是内层枚举的case值,第1位的byte值通常是0
    2.2 最后一个入参的byte空间分布 👉 第2位是内层枚举的case值,第1位是外层枚举的case值,其规律又如下👇
  • 当外层enum的case只有2个时,第1位byte值按照0、8依次分布
  • 当外层enum的case个数>2,<=4时,第1位byte值按照0、4、8、12依次分布
  • 当外层enum的case个数>4时,第1位byte值按照0、1、2、3、...依次分布

5.4 struct嵌套enum

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 .up, .down:
            print("up, down")
        }
    }
}
print(MemoryLayout<Skill>.size)
print(MemoryLayout<Skill>.stride)

size和stride都是1。结构体的大小计算,跟函数无关,所以只看成员变量key的大小,key是枚举Skill类型,大小为1,所以结构体大小为1。继续,去掉成员key👇

没有任何成员变量时,size为0,stride为1(系统默认分配的)。如果加一个成员👇

因为添加的是UInt8,占1个字节,所以size和stride都+1,均为2。再添加一个成员👇

添加的成员是Int类型,占8字节,8+1+1=10,而stride是系统分配的,8的倍数来分配,所以是16。你以为就这么简单的相加吗?我们换一下width的位置👇

将width成员放到最后面,size变为16,why?因为size的大小,是按照结构体内存对齐原则来计算的,可参考我之前的文章内存对齐分析

总结

本篇文章主要讲解了Swift中的枚举,开始与OC的枚举作比较,引出Swift枚举的不同点,进而分析了rawValue和初始化init的底层实现流程,然后讲解了几个重要的场景 👉 OC和Swift的桥接场景,枚举嵌套的场景,最后重点分析了枚举的大小,即内存分布的情况,这也是面试中经常出的题目,希望大家掌握,谢谢!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • C语言枚举 一周七天可以写成 第⼀个枚举成员的默认值为整型的 0,后⾯的枚举值依次类推,如果我们想更改,只需要这样...
    Mjs阅读 225评论 0 1
  • C语言的枚举 C语言的枚举写法 我们通过枚举表示一周的七天 c语言中,枚举的第一个成员默认是为0,后面的枚举值一次...
    浪的出名阅读 380评论 0 1
  • 首先感觉Swift的枚举确实相对OC来说功能和语法都扩展很多。首先说一下枚举的定义。 枚举声明的类型是囊括可能状态...
    正直走阅读 292评论 0 0
  • 鉴于昨天开会部门会议讨论的时候,发现有些朋友对枚举的用法还是存在一些疑问,所以就写下这个文章,介绍下Swift下的...
    AndreaArlex阅读 52,882评论 14 68
  • Swift中不仅提供了 面向过程的编程支持,也提供了全面的面向对象的支持,与普通的面向对象的编程语言(只支持类)不...
    随梦而飞飞阅读 5,166评论 3 4