Swift系列四 - 枚举
适度给类型起别名能够让代码更加易懂,开发效率更高,可维护性更好。
一、typealias(别名)
typealias用来给类型起别名。
typealias Byte = Int8
typealias Short = Int16
typealias Long = Int64
typealias Date = (year: Int, month: Int, day: Int)
func test(_ date: Date) {
print(date.year)
}
test((2019, 6, 25))
// 输出:2019
typealias IntFn = (Int, Int) -> Int
func diff(v1: Int, v2: Int) -> Int {
v1 - v2
}
let fn: IntFn = diff
fn(10, 5)
// 输出:5
Void的本质就是空元祖的别名:public typealias Void = ()
二、枚举
Swift枚举和C/OC语言不一样,以前写OC的时候枚举本质是int类型,但Swift中枚举可以是多类型的。
官方建议:枚举名称使用大写,成员使用小写。
2.1. 基本用法
// 定义枚举
enum YBColor {
case white
case black
case gray
}
// 等价于上面的代码
//enum YBColor {
// case white, black, gray
//}
var color = YBColor.white
color = YBColor.black
color = .gray // 简写(因为此时已经确定变量color是YBColor类型)
print(color) // 输出:gray
// 循环控制
switch color {
case .white:
print("white")
case .black:
print("black")
case .gray:
print("gray")
}
2.2. 关联值
有时将枚举的成员值跟其他类型的值关联存储在一起,会非常有用.
案例:
enum Score {
case points(Int)
case grade(Character)
}
// 数值表达
var score = Score.points(96)
// 等级/字符表达
score = .grade("A")
enum Date {
case digit(year: Int, month: Int, day: Int)
case string(String)
}
var date = Date.digit(year: 2019, month: 06, day: 25)
date = .string("2019-06-25")
switch date {
case .digit(let year, let month, let day):
print(year, month, day, separator:"/")
case let .string(value):
print(value)
}
/*
输出:
2019-06-25
*/
let写在枚举成员前面意味着枚举成员形参只能是常量,放在形参里面可以自定义选择是var还是let。
2.2. 原始值
枚举成员可以使用相同类型的默认值预先关联,这个默认值叫做:原始值。
enum Direction : Character {
case up = "w"
case down = "s"
case left = "a"
case right = "d"
}
var direction = Direction.up
print(direction) // 输出:up
print(direction.rawValue) // 输出:w
print(Direction.down.rawValue) // 输出:s
如果枚举的原始值类型是Int、String,Swift会自动分配原始值:
enum Direction : String {
case up = "up"
case down = "down"
case left = "left"
case right = "right"
}
var direction = Direction.up
print(direction) // 输出:up
print(direction.rawValue) // 输出:up
print(Direction.down.rawValue) // 输出:down
// 等价
enum Direction : String {
case up, down, left, right
}
var direction = Direction.up
print(direction) // 输出:up
print(direction.rawValue) // 输出:up
print(Direction.down.rawValue) // 输出:down
Int类型,成员值自增(类似C/OC枚举):
enum Season : Int {
case spring, summer, autumn, winter
}
print(Season.spring.rawValue) // 输出:0
print(Season.summer.rawValue) // 输出:1
print(Season.autumn.rawValue) // 输出:2
print(Season.winter.rawValue) // 输出:3
enum Season : Int {
case spring = 1, summer, autumn = 4, winter
}
print(Season.spring.rawValue)
print(Season.summer.rawValue)
print(Season.autumn.rawValue)
print(Season.winter.rawValue)
2.3. 递归枚举
- 关键字:
indirect; - 可以把需要递归枚举的成员前面加
indirect,也可以为了方便直接加到枚举定义前面。
indirect enum ArithExpr {
case number(Int)
case sum(ArithExpr, ArithExpr)
case diff(ArithExpr, ArithExpr)
}
//enum ArithExpr {
// case number(Int)
// indirect case sum(ArithExpr, ArithExpr)
// indirect case diff(ArithExpr, ArithExpr)
//}
let five = ArithExpr.number(5)
let four = ArithExpr.number(4)
let two = ArithExpr.number(2)
let sum = ArithExpr.sum(five, four)
let diff = ArithExpr.diff(sum, two)
func cal(_ expr: ArithExpr) -> Int {
switch expr {
case let .number(value):
return value
case let .sum(left, right):
return cal(left) + cal(right)
case let .diff(left, right):
return cal(left) - cal(right)
}
}
cal(diff) // 输出:7
三、枚举的内存布局
在Swift中查看内存占用大小及对齐方式使用枚举:MemoryLayout:
- size:实际用到的空间大小
- stride:分配占用的空间大小
- alignment:内存对齐方式
下面的意思是,Int在内存中占用8个字节,内存对齐数是8:
MemoryLayout<int>.size // 输出:8
MemoryLayout<int>.stride // 输出:8
MemoryLayout<int>.alignment // 输出:8
查看枚举占用内存:
enum Password {
case number(Int, Int, Int, Int)
case other
}
var pwd = Password.number(1, 2, 2, 3)
MemoryLayout.size(ofValue: pwd) // 输出:33
MemoryLayout.stride(ofValue: pwd) // 输出:40
MemoryLayout.alignment(ofValue: pwd) // 输出:8
为什么是占用内存大小是33,而分配了40?
number(Int, Int, Int, Int)占用32个字节,other占用1个字节,所以一共只需要占用33个字节就够用了- 因为内存对齐数是8,所以分配内存的时候只能是8的倍数,而33个字节不够8的倍数,所以往高位补齐后就是40了
为什么other占用1个字节呢?
enum Season {
case spring, summer, autumn, winter
}
MemoryLayout<season>.size // 输出:1
MemoryLayout<season>.stride // 输出:1
MemoryLayout<season>.alignment // 输出:1
// 限定类型
enum Season: String {
case spring, summer, autumn, winter
}
MemoryLayout<season>.size // 输出:1
MemoryLayout<season>.stride // 输出:1
MemoryLayout<season>.alignment // 输出:1
- 上面代码可以看出不管类型是什么占用的内存大小都是1个字节;
- 其实本质上是关联值和原始值的区别。
结论一: 把传进去的关联值直接存储到枚举变量内存里面的,所以枚举变量是关联值的话,内存是一定和将要存储的关联值大小有关。
为了证实结论一,比较下面的两个不同类型的关联值:
enum Password {
case number(Int, Int, Int, Int)
case other
}
MemoryLayout<password>.size // 输出:33
MemoryLayout<password>.stride // 输出:40
MemoryLayout<password>.alignment // 输出:8
enum Password {
case number(String, String, String, String)
case other
}
MemoryLayout<password>.size // 输出:65
MemoryLayout<password>.stride // 输出:72
MemoryLayout<password>.alignment // 输出:8
结论二: 原始值固定后是不能修改的,内存中只会把对应的成员值(序号)存下来,这时候1个字节足够用了,和枚举类型无关(不管是Int还是String枚举都是占用一个字节)。
分析下面代码:
enum Season: Int {
// 序号0 序号1 序号2 序号3
case spring = 1, summer = 2, autumn = 3, winter = 4
}
var season1 = Season.spring
var season2 = Season.spring
var season3 = Season.spring
MemoryLayout<season>.size // 输出:1
MemoryLayout<season>.stride // 输出:1
MemoryLayout<season>.alignment // 输出:1
疑问:成员值在内存中只占用1个字节,Int或String的原始值是怎么存下的?rawValue其实是另外一块地址。
- 关联值才会存储到枚举变量中,原始值不会占用枚举变量内存
- 我们可以通过内存地址看到前面的字节被关联值占用,关联值后面有一个字节是保存成员值
- 1个字节存储成员值(如果只有一个枚举成员则不占用内存)
- N个字节存储关联值(N取占用内存最大的关联值),任何一个case的关联值都共用这N个字节(共用体)
- 剩余字节按照对齐数补齐
Switch的case其实是比较枚举的成员值的

Swift系列四 - 枚举的更多相关文章
- 窥探Swift系列博客说明及其Swift版本间更新
Swift到目前为止仍在更新,每次更新都会推陈出新,一些Swift旧版本中的东西在新Swift中并不适用,而且新版本的Swift会添加新的功能.到目前为止,Swift为2.1版本.去年翻译的Swift ...
- Swift 学习- 09 -- 枚举
// 递归枚举 // 美家居为一组相关的值定义了一个共同的类型, 使你可以在代码中以类型安全的的方式使用这些值. // 如果你熟悉C语言, 你会知道在C语言中, 枚举会为一组整型值分配相关联的名称, ...
- Swift 学习笔记 (枚举)
枚举为一种相关值定义了一个通用类型,从而可以让你在代码中类型安全的操作这些值. Swift中的枚举很灵活,不需要给每一个枚举中的成员都提供值.如果一个值(所谓 原时值) 要被提供给每一个枚举成员,那么 ...
- C#中的函数式编程:递归与纯函数(二) 学习ASP.NET Core Razor 编程系列四——Asp.Net Core Razor列表模板页面
C#中的函数式编程:递归与纯函数(二) 在序言中,我们提到函数式编程的两大特征:无副作用.函数是第一公民.现在,我们先来深入第一个特征:无副作用. 无副作用是通过引用透明(Referential ...
- 前端构建大法 Gulp 系列 (四):gulp实战
前端构建大法 Gulp 系列 (一):为什么需要前端构建 前端构建大法 Gulp 系列 (二):为什么选择gulp 前端构建大法 Gulp 系列 (三):gulp的4个API 让你成为gulp专家 前 ...
- Netty4.x中文教程系列(四) 对象传输
Netty4.x中文教程系列(四) 对象传输 我们在使用netty的过程中肯定会遇到传输对象的情况,Netty4通过ObjectEncoder和ObjectDecoder来支持. 首先我们定义一个U ...
- S5PV210开发系列四_uCGUI的移植
S5PV210开发系列四 uCGUI的移植 象棋小子 1048272975 GUI(图形用户界面)极大地方便了非专业用户的使用,用户无需记忆大量的命令,取而代之的是能够通过窗体.菜单 ...
- WCF编程系列(四)配置文件
WCF编程系列(四)配置文件 .NET应用程序的配置文件 前述示例中Host项目中的App.config以及Client项目中的App.config称为应用程序配置文件,通过该文件配置可控制程序的 ...
- SQL Server 2008空间数据应用系列四:基础空间对象与函数应用
原文:SQL Server 2008空间数据应用系列四:基础空间对象与函数应用 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Server 2008 R2调测. ...
随机推荐
- 锐捷RG-UAC统一上网行为管理审计系统账号密码泄露漏洞 CNVD-2021-14536
一:产品介绍: 锐捷 RG-UAC 统一上网行为管理审计系统 锐捷统一上网行为管理与审计RG-UAC系列是星网锐捷网络有限公司自主研发的上网行为管理与审计产品,以路由.透明.旁路或混合模式部署在网络的 ...
- 谈谈注册中心 zookeeper 和 eureka中的CP和 AP
谈谈注册中心 zookeeper 和 eureka中的CP和 AP 前言 在分布式架构中往往伴随CAP的理论.因为分布式的架构,不再使用传统的单机架构,多机为了提供可靠服务所以需要冗余数据因而会存在分 ...
- 常见 git 需求整理(持续更新中)
首发于 语雀文档 突然感觉自己对 git 还是挺熟悉的,因为团队里新来的七八号应届生来问我 git 问题,基本没有答不上的情况,但为了能更好地对知识进行整理,还是记录一下为好. (希望能)持续更新.. ...
- Apache配置 8.配置防盗链
(1)介绍 防盗链,通俗讲,就是不让别人盗用你网站上的资源.这个资源,通常指的是图片.视频.歌曲.文档等. (2)配置 配置防盗链先编辑主机配置文件: #vim /usr/local/apache2. ...
- BIMFACE二次开发【C#系列】
本系列文章主要介绍使用 C# .ASP.NET(MVC)技术对 BIMFACE 平台进行二次开发,以满足本公司针对建筑行业施工图审查系统的业务需求,例如图纸模型(PDF 文件.二维 CAD 模型.三维 ...
- BeanShell 用法汇总
一.什么是Bean Shell BeanShell是一种完全符合Java语法规范的脚本语言,并且又拥有自己的一些语法和方法; BeanShell是一种松散类型的脚本语言(这点和JS类似); BeanS ...
- python学习之web中的html基础
HTML简介及相关标签介绍 首先,我们要了解什么是HTML? HTML被称作超文本标记语言,是一门标记语言,即该语言由各类标记标签组成. 为什么学python要学HTML? 不管学习哪个语言,我们最 ...
- Windows包管理器——Scoop 包管理器
Scoop 包管理器 目录 Scoop 包管理器 参考 官方 博客 声明 目录 scoop 安装&&卸载 安装( 使用 powershell) 卸载(软件的使用权归自己所有,一言不合即 ...
- ASP.NET Core中间件初始化探究
前言 在日常使用ASP.NET Core开发的过程中我们多多少少会设计到使用中间件的场景,ASP.NET Core默认也为我们内置了许多的中间件,甚至有时候我们需要自定义中间件来帮我们处理一些请求管道 ...
- IdentityServer4是什么
1 什么是IdentityServer4? IdentityServer4是用于ASP.NET Core的OpenID Connect和OAuth 2.0框架. 2 什么是OAuth 2.0? OAu ...