经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » iOS » 查看文章
Swift系列四 - 枚举
来源:cnblogs  作者:1024星球  时间:2021/5/10 8:58:07  对本文有异议

适度给类型起别名能够让代码更加易懂,开发效率更高,可维护性更好。

一、typealias(别名)

typealias用来给类型起别名。

  1. typealias Byte = Int8
  2. typealias Short = Int16
  3. typealias Long = Int64
  4. typealias Date = (year: Int, month: Int, day: Int)
  5. func test(_ date: Date) {
  6. print(date.year)
  7. }
  8. test((2019, 6, 25))
  9. // 输出:2019
  10. typealias IntFn = (Int, Int) -> Int
  11. func diff(v1: Int, v2: Int) -> Int {
  12. v1 - v2
  13. }
  14. let fn: IntFn = diff
  15. fn(10, 5)
  16. // 输出:5

Void的本质就是空元祖的别名:public typealias Void = ()

二、枚举

Swift枚举和C/OC语言不一样,以前写OC的时候枚举本质是int类型,但Swift中枚举可以是多类型的。

官方建议:枚举名称使用大写,成员使用小写。

2.1. 基本用法

  1. // 定义枚举
  2. enum YBColor {
  3. case white
  4. case black
  5. case gray
  6. }
  7. // 等价于上面的代码
  8. //enum YBColor {
  9. // case white, black, gray
  10. //}
  11. var color = YBColor.white
  12. color = YBColor.black
  13. color = .gray // 简写(因为此时已经确定变量color是YBColor类型)
  14. print(color) // 输出:gray
  15. // 循环控制
  16. switch color {
  17. case .white:
  18. print("white")
  19. case .black:
  20. print("black")
  21. case .gray:
  22. print("gray")
  23. }

2.2. 关联值

有时将枚举的成员值其他类型的值关联存储在一起,会非常有用.

案例:

  1. enum Score {
  2. case points(Int)
  3. case grade(Character)
  4. }
  5. // 数值表达
  6. var score = Score.points(96)
  7. // 等级/字符表达
  8. score = .grade("A")
  9. enum Date {
  10. case digit(year: Int, month: Int, day: Int)
  11. case string(String)
  12. }
  13. var date = Date.digit(year: 2019, month: 06, day: 25)
  14. date = .string("2019-06-25")
  15. switch date {
  16. case .digit(let year, let month, let day):
  17. print(year, month, day, separator:"/")
  18. case let .string(value):
  19. print(value)
  20. }
  21. /*
  22. 输出:
  23. 2019-06-25
  24. */

let写在枚举成员前面意味着枚举成员形参只能是常量,放在形参里面可以自定义选择是var还是let

2.2. 原始值

枚举成员可以使用相同类型的默认值预先关联,这个默认值叫做:原始值。

  1. enum Direction : Character {
  2. case up = "w"
  3. case down = "s"
  4. case left = "a"
  5. case right = "d"
  6. }
  7. var direction = Direction.up
  8. print(direction) // 输出:up
  9. print(direction.rawValue) // 输出:w
  10. print(Direction.down.rawValue) // 输出:s

如果枚举的原始值类型是IntString,Swift会自动分配原始值:

  1. enum Direction : String {
  2. case up = "up"
  3. case down = "down"
  4. case left = "left"
  5. case right = "right"
  6. }
  7. var direction = Direction.up
  8. print(direction) // 输出:up
  9. print(direction.rawValue) // 输出:up
  10. print(Direction.down.rawValue) // 输出:down
  11. // 等价
  12. enum Direction : String {
  13. case up, down, left, right
  14. }
  15. var direction = Direction.up
  16. print(direction) // 输出:up
  17. print(direction.rawValue) // 输出:up
  18. print(Direction.down.rawValue) // 输出:down

Int类型,成员值自增(类似C/OC枚举):

  1. enum Season : Int {
  2. case spring, summer, autumn, winter
  3. }
  4. print(Season.spring.rawValue) // 输出:0
  5. print(Season.summer.rawValue) // 输出:1
  6. print(Season.autumn.rawValue) // 输出:2
  7. print(Season.winter.rawValue) // 输出:3
  8. enum Season : Int {
  9. case spring = 1, summer, autumn = 4, winter
  10. }
  11. print(Season.spring.rawValue)
  12. print(Season.summer.rawValue)
  13. print(Season.autumn.rawValue)
  14. print(Season.winter.rawValue)

2.3. 递归枚举

  • 关键字:indirect
  • 可以把需要递归枚举的成员前面加indirect,也可以为了方便直接加到枚举定义前面。
  1. indirect enum ArithExpr {
  2. case number(Int)
  3. case sum(ArithExpr, ArithExpr)
  4. case diff(ArithExpr, ArithExpr)
  5. }
  6. //enum ArithExpr {
  7. // case number(Int)
  8. // indirect case sum(ArithExpr, ArithExpr)
  9. // indirect case diff(ArithExpr, ArithExpr)
  10. //}
  11. let five = ArithExpr.number(5)
  12. let four = ArithExpr.number(4)
  13. let two = ArithExpr.number(2)
  14. let sum = ArithExpr.sum(five, four)
  15. let diff = ArithExpr.diff(sum, two)
  16. func cal(_ expr: ArithExpr) -> Int {
  17. switch expr {
  18. case let .number(value):
  19. return value
  20. case let .sum(left, right):
  21. return cal(left) + cal(right)
  22. case let .diff(left, right):
  23. return cal(left) - cal(right)
  24. }
  25. }
  26. cal(diff) // 输出:7

三、枚举的内存布局

在Swift中查看内存占用大小及对齐方式使用枚举:MemoryLayout

  • size:实际用到的空间大小
  • stride:分配占用的空间大小
  • alignment:内存对齐方式

下面的意思是,Int在内存中占用8个字节,内存对齐数是8:

  1. MemoryLayout<int>.size // 输出:8
  2. MemoryLayout<int>.stride // 输出:8
  3. MemoryLayout<int>.alignment // 输出:8

查看枚举占用内存:

  1. enum Password {
  2. case number(Int, Int, Int, Int)
  3. case other
  4. }
  5. var pwd = Password.number(1, 2, 2, 3)
  6. MemoryLayout.size(ofValue: pwd) // 输出:33
  7. MemoryLayout.stride(ofValue: pwd) // 输出:40
  8. MemoryLayout.alignment(ofValue: pwd) // 输出:8

为什么是占用内存大小是33,而分配了40?

  • number(Int, Int, Int, Int)占用32个字节,other占用1个字节,所以一共只需要占用33个字节就够用了
  • 因为内存对齐数是8,所以分配内存的时候只能是8的倍数,而33个字节不够8的倍数,所以往高位补齐后就是40了

为什么other占用1个字节呢?

  1. enum Season {
  2. case spring, summer, autumn, winter
  3. }
  4. MemoryLayout<season>.size // 输出:1
  5. MemoryLayout<season>.stride // 输出:1
  6. MemoryLayout<season>.alignment // 输出:1
  7. // 限定类型
  8. enum Season: String {
  9. case spring, summer, autumn, winter
  10. }
  11. MemoryLayout<season>.size // 输出:1
  12. MemoryLayout<season>.stride // 输出:1
  13. MemoryLayout<season>.alignment // 输出:1
  • 上面代码可以看出不管类型是什么占用的内存大小都是1个字节;
  • 其实本质上是关联值和原始值的区别。

结论一: 把传进去的关联值直接存储到枚举变量内存里面的,所以枚举变量是关联值的话,内存是一定和将要存储的关联值大小有关。

为了证实结论一,比较下面的两个不同类型的关联值:

  1. enum Password {
  2. case number(Int, Int, Int, Int)
  3. case other
  4. }
  5. MemoryLayout<password>.size // 输出:33
  6. MemoryLayout<password>.stride // 输出:40
  7. MemoryLayout<password>.alignment // 输出:8
  8. enum Password {
  9. case number(String, String, String, String)
  10. case other
  11. }
  12. MemoryLayout<password>.size // 输出:65
  13. MemoryLayout<password>.stride // 输出:72
  14. MemoryLayout<password>.alignment // 输出:8

结论二: 原始值固定后是不能修改的,内存中只会把对应的成员值(序号)存下来,这时候1个字节足够用了,和枚举类型无关(不管是Int还是String枚举都是占用一个字节)。

分析下面代码:

  1. enum Season: Int {
  2. // 序号0 序号1 序号2 序号3
  3. case spring = 1, summer = 2, autumn = 3, winter = 4
  4. }
  5. var season1 = Season.spring
  6. var season2 = Season.spring
  7. var season3 = Season.spring
  8. MemoryLayout<season>.size // 输出:1
  9. MemoryLayout<season>.stride // 输出:1
  10. MemoryLayout<season>.alignment // 输出:1

疑问:成员值在内存中只占用1个字节,Int或String的原始值是怎么存下的?rawValue其实是另外一块地址。

  • 关联值才会存储到枚举变量中,原始值不会占用枚举变量内存
  • 我们可以通过内存地址看到前面的字节被关联值占用,关联值后面有一个字节是保存成员值
    • 1个字节存储成员值(如果只有一个枚举成员则不占用内存)
    • N个字节存储关联值(N取占用内存最大的关联值),任何一个case的关联值都共用这N个字节(共用体)
    • 剩余字节按照对齐数补齐

Switchcase其实是比较枚举的成员值的

原文链接:http://www.cnblogs.com/idbeny/p/swift-syntax-enum.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号