一、基本使用

先看枚举的几种使用(暂不要问,看看是否都能看懂,待会会逐一讲解)

1、操作一 简单使用

//第一种方式
enum Direction {
case east
case west
case south
case north func testDir() -> String {
switch self {
case .east:
return "东边"
case .west:
return "西边"
case .south:
return "南边"
case .north:
return "北边"
}
}
} //第二种方式
enum Direction1 {
case east, west, south, north func testDir() -> String {
switch self {
case .east:
return "东边"
case .west:
return "西边"
case .south:
return "南边"
case .north:
return "北边"
}
}
} var dir = Direction.east
dir = .north
var dir1 = Direction1.east
dir1 = .north

第一种和第二种完全一样。

2、操作二 关联值(Associated Values)

关联值(Associated Values)将枚举的成员值跟其他类型的值关联存储在一起,非常有用!

2.1 关联值示例1

enum Score {
case points(Int)
case grade(Character)
}
var score = Score.points()
score = .grade("A") func testScore() {
switch score {
case let .points(i):
print(i,"points")
case let .grade(i):
print("grade",i)
}
}

2.2 关联值示例2

enum Date {
case digit(year: Int, month: Int, day: Int)
case string(String)
} var date = Date.digit(year: , month: , day: )
date = .string("2011-09-10") func testDate() {
switch date {
case .digit(let year, let month, let day):
print(year, month, day)
case let .string(value):
print(value)
}
}

2.3 关联值示例3

手机密码方式有以下两种,可以用枚举表示,用关联值表示,图如下:

上面的图可以用关联值枚举表示如下

enum Password {
case number(Int, Int, Int, Int)
case gesture(String)
} var pwd = Password.number(, , , )
pwd = .gesture("") func testPwd() {
switch pwd {
case let .number(n1, n2, n3, n4):
print("number is ", n1, n2, n3, n4)
case let .gesture(str):
print("gesture is " ,str)
}
}

3、操作三 原始值(Raw Values)

原始值: 枚举成员可以使用相同类型的默认值预先对应,默认值叫做原始值,原始值不占用枚举变量的内存

enum PokerSuit: Character {
case spade = "黑"
case heart = "红"
case diamond = "方"
case club = "花"
} var suit = PokerSuit.spade
print(suit.rawValue)//黑
print(PokerSuit.club.rawValue)//花

4、隐式原始值(Implicitly Assigned Raw Values)

如果枚举的原始值是Int,String,Swift会自动分配原始值,和成员值一样

4.1 隐式原始值示例1

Direction和Direction1意义是一样的

enum Direction: String {
case north = "north"
case south = "south"
case east = "east"
case west = "west"
} enum Direction1: String {
case north, south, east, west
} print(Direction.north) //north
print(Direction1.north.rawValue)// north

4.2 隐式原始值示例2

Int类型,默认为0开始

enum Season: Int {
case spring, summer, autumn, winter
}
print(Season.spring.rawValue) //
print(Season.summer.rawValue) //
print(Season.autumn.rawValue) //
print(Season.winter.rawValue) //

4.3 隐式原始值示例3

enum Season: Int {
case spring = , summer, autumn = , winter
}
print(Season.spring.rawValue) //
print(Season.summer.rawValue) //
print(Season.autumn.rawValue) //
print(Season.winter.rawValue) //

上面讲述枚举的基本使用,下面我们将进入核心内容-从汇编的角度来看枚举的内存!!

二、汇编角度看枚举内存

示例1: 简单实用-通过下面代码查看枚举实例占用多少内存字节等

enum TestEnum {
case test1, test2, test3
} var t = TestEnum.test1
t = .test2
t = .test3

通过MemoryLayout查看内存大小

enum TestEnum {
case test1, test2, test3
} var t = TestEnum.test1
t = .test2
t = .test3
print(MemoryLayout<TestEnum>.size) //TestEnum实际占用内存空间
print(MemoryLayout<TestEnum>.stride)//系统分配给TestEnum的内存空间
print(MemoryLayout<TestEnum>.alignment)//对齐参数

运行结果如下

其实Swift还是很聪明的,仅仅使用一个字节来判断对象的不同,下面窥探test1,test2,test3的内存

因为Swift不支持枚举看底层的,所以通过一个内存访问小工具查看内存地址,然后通过内存地址查看内存布局

拿到内存地址后,可以通过view memory查看内容

将地址0x0000000100006660输入进去

因为通过上面发现占用一个字节,所以看第一个字节存储的为00,t为test1时

将断点向后移,看t = test2时,t的内存存储的时

再次看下t = test2 内存存储的值为

最后看下t = test3内存存储为

这种形式的枚举定义形式占用一个字节,可以代表的枚举范围也就是0x00-0xFF共256个case,足以表示所有情况的枚举穷举啦!

从示例1中,当枚举里面仅仅是case多个对象,枚举内存仅仅会分配1个字节来存储各个case,case对应的为0,1,2……

示例2 带有原始值

enum TestEnum: Int {
case test1 = , test2 = , test3 =
}
var t = TestEnum.test1
t = .test2
t = .test3

观察上面带有原始值枚举分配内存和占用内存情况

从最上面讲述带有原始值的枚举(红色标记)原始值不占用枚举变量的内存

所以仅仅需要1个字节来区分test1, test2,test3,我们再来看一个test2,看内存存储的是多少

看出test2存储的是依然是1,和原始值内容没有任何关系,存储和示例1没有区别,再次印证了,原始值不占用枚举变量的内存,不影响枚举内存结构和存储

示例3 带有关联值的枚举内存结构

关联值从上面基本使用得出关联值(Associated Values)将枚举的成员值跟其他类型的值关联存储在一起

enum TestEnum {
case test1(Int, Int, Int)
case test2(Int, Int)
case test3(Int)
case test4(Bool)
case test5
}
var t = TestEnum.test1(, , )
t = .test2(, )
t = .test3()
t = .test4(true)
t = .test5

继续使用MemoryLayout来看内存分配

从上面可看出TestEnum枚举实际占用内存空间大小为25,又因为内存对齐为8,所以系统分配了32个字节的大小给TestEnum

下面着重讲解为什么实际占用了25个字节,又是怎么存储的?

通过内存小工具查看枚举地址

然后View Memory工具查看内存结构如下

上面得出Int占据8个字节,对于TestEnum.test1(1, 2, 3)用24个字节存储这些关联值,得出关联值(Associated Values)将枚举的成员值跟其他类型的值关联存储在一的结论是正确的!

(拓展:为什么01,02放在前面,为什么不是放在后面,这牵扯到大小端的问题?下面讲述)

下面看test2的存储结构t = .test2(4, 5)

看第25个字节为Test2为0x01 = 1, test1的第25个字节为0x00 = 0, 依次类推,查看test4应该为3,下面揭开谜底

关联值枚举存储结论

有一个字节存储成员值,用于区分哪一个成员值

N个字节存储关联值(N取占用内存量最大的关联值),任何一个case的关联值都会共用这N个字节

上面代码与查看内存小工具封装代码https://github.com/zxy1829760/SwiftEnum

 拓展-大小端问题

存储0x11223344,大小端存储如下

以上就是枚举内存的底层结构,希望对大家有所帮助!!! 下一篇将讲述struct与class的区别!

Swift 枚举-从汇编角度看枚举内存结构的更多相关文章

  1. 从linux进程角度看JVM内存模型

    普通进程栈区,在JVM一般仅仅用做线程栈,如下图所示 首先是永久代.永久代本质上是Java程序的代码区和数据区.Java程序中类(class),会被加载到整个区域的不同数据结构中去,包括常量池.域.方 ...

  2. Swift 里 Array (一)内存结构

    public struct Array<Element>: _DestructorSafeContainer { #if _runtime(_ObjC) @usableFromInline ...

  3. c++ 汇编代码看内存分配

    汇编代码看内存分配 (1). 程序运行时分为存储区域分为 存储区域 存储内容 extra 代码区 存放代码指令,包括除字符串常量的字面值 静态存储区 存放静态变量和全局变量 执行main之前就分配好了 ...

  4. Swift--struct与class的区别(汇编角度底层分析)

    概述 相对Objective-C, Swift使用结构体Struct的比例大大增加了,其中Int, Bool,以及String,Array等底层全部使用Struct来定义!在Swift中结构体不仅可以 ...

  5. swift_枚举 | 可为空类型 | 枚举关联值 | 枚举递归 | 树的概念

    ***************可为空的类型 var demo2 :we_demo = nil 上面这个代码串的语法是错的 为什么呢, 在Swift中,所有的类型定义出来的属性的默认值都不可以是nil ...

  6. java枚举与.net中的枚举区别

    通过一段时间的项目实践,发现java中的枚举与.net中的枚举有很大的差别,初期造成了我对java中的枚举一些错误理解及部分有缺陷的应用,其实追其原因还是因为我会习惯性的认为java的枚举在作用以及定 ...

  7. 从free命令看Linux内存管理

    free命令是Linux系统下用来查看内存使用情况的,例如: $ free -h total used free shared buffers cached Mem: 7.8G 6.6G 1.3G 0 ...

  8. 从线程模型的角度看Netty的高性能

    转载:Netty(二) 从线程模型的角度看 Netty 为什么是高性能的? 传统 IO 在 Netty 以及 NIO 出现之前,我们写 IO 应用其实用的都是用 java.io.* 下所提供的包. 比 ...

  9. 从JDK源码角度看Object

    Java的Object是所有其他类的父类,从继承的层次来看它就是最顶层根,所以它也是唯一一个没有父类的类.它包含了对象常用的一些方法,比如getClass.hashCode.equals.clone. ...

随机推荐

  1. 关于爬虫的日常复习(7)—— DOM操作及selenium库

  2. Go 每日一库之 fsnotify

    简介 上一篇文章Go 每日一库之 viper中,我们介绍了 viper 可以监听文件修改进而自动重新加载. 其内部使用的就是fsnotify这个库,它是跨平台的.今天我们就来介绍一下它. 快速使用 先 ...

  3. ASP.NET 开源导入导出库Magicodes.IE 导出Pdf教程

    基础教程之导出Pdf收据 说明 本教程主要说明如何使用Magicodes.IE.Pdf完成Pdf收据导出 要点 导出PDF数据 自定义PDF模板 导出单据 如何批量导出单据 导出特性 PdfExpor ...

  4. [状压DP思路妙题]图

    源自 luhong 大爷的 FJ 省冬令营模拟赛题 Statement 给定一个 \(n\) 个点 \(m\) 条边的图,没有重边与自环 每条边的两端点编号之差不超过 \(12\) 求选出一个非空点集 ...

  5. [bzoj3925] [洛谷P3343] [ZJOI2015] 地震后的幻想乡

    Description 傲娇少女幽香是一个很萌很萌的妹子,而且她非常非常地有爱心,很喜欢为幻想乡的人们做一些自己力所能及的事情来帮助他们. 这不,幻想乡突然发生了地震,所有的道路都崩塌了.现在的首要任 ...

  6. Windows安装Python环境和Python集成开发环境(IDE)PyCharm

    1.Windows中安装Python 3 (1)打开浏览器,访问Python官网(https://www.python.org/) (2)光标移动至Downloads,单机Windows链接 (3)根 ...

  7. 第二阶段冲刺个人任务——three

    今日任务: 优化统计个人博客结果页面的显示. 昨日成果: 优化作业查询结果,按学号排列.

  8. 4、python基础语法

    前言:本文主要介绍python的一些基础语法,包括标识符的定义.行和缩进.引号和注释.输入输出.变量的定义. 一.标识符 1.凡是我们自己取的名字,都是标识符. 2.在Python里,标识符由字母.下 ...

  9. [白话解析] 深入浅出支持向量机(SVM)之核函数

    [白话解析] 深入浅出支持向量机(SVM)之核函数 0x00 摘要 本文在少用数学公式的情况下,尽量仅依靠感性直觉的思考来讲解支持向量机中的核函数概念,并且给大家虚构了一个水浒传的例子来做进一步的通俗 ...

  10. Oracle 数据泵(IMPDP/EXPDP)导入导出总结

    Oracle数据泵导入导出是日常工作中常用的基本技术之一,它相对传统的逻辑导入导出要高效,这种特性更适合数据库对象数量巨大的情形,因为我日常运维的数据库对象少则几千,多则几万甚至几十万,所以传统exp ...