美文网首页
枚举可选型

枚举可选型

作者: AndyYaWei | 来源:发表于2020-08-09 14:01 被阅读0次

    开始今天的内容之前先补充个关键字:
    @inline

    @inline

    内敛函数
    如果开启了编译器优化(Release模式默认开启优化),编译器会自动将某些函数变成内敛函数.

    image.png

    哪些函数不会被自动内敛?
    1、代码比较长
    2、递归调用函数
    3、动态派发函数

    可以通过never、__always关键字控制一下内敛:

    • 永远不会被内敛(即便开启了编译器优化)
    @inline(never) func test() {
         print("test")
     }
    
    • 开启编译器优化后,即使代码比较长,也会被内敛(递归调用函数、动态派发函数除外)
    @inline(__always) func test() {
          print("test")
     }
    

    release环境下,编译器已经开启优化,会自动决定哪些函数需要内敛,因此没必要使用@inline

    枚举的基本用法

    回顾一下以前的C语言下枚举的本质就是整型(0、1、2 ...)

    enum Score {
        case one
        case two
        case three
    } 
    let e = Score.one 
    

    关联值(Associated Values)

    案例1

    var score = Score.point(96)
    score = Score.grade("A")
    
    switch score {
    case let .point(i):
        print(i,"points")
    case let .grade(i):
        print("grade", i)
    }
    

    案例2

    enum Date {
        case digit(year: Int, month: Int, day: Int)
        case string(String)
    }
    
    var date = Date.digit(year: 2020, month: 6, day: 26)
    date = Date.string("2020-06-26")
    
    switch date {
    case .digit(year: let year, month: let month, day: let day):
        print(year, month, day)
    case let .string(value)
        print(value)
    } 
    

    结论,关联值将各种类型的数据直接存储在枚举变量里面去。

    原始值(Raw Values)

    enum PokerSuit: Character {
        case spade = "♠️"
        case heart = "♥️"
        case diamond = "♦️"
        case club = "♣️"
    }
    

    枚举成员可以使用相同类型的默认值进行关联,这个默认值叫做:原始值
    注:这里的冒号不是继承哈!

    使用如下:

    var suit = PokerSuit.spade
    print(suit)// spade
    print(suit.rawValue)//♠️
    print(PokerSuit.club.rawValue)//♣️
    

    隐式原始值(Implicitly Assigned Raw Values)

    如果枚举的原始值类型是Int、String,Swift会自动分配原始值
    如下:

    enum Direction: String {
       case north = "north"
       case south = "south"
       case east = "east"
       case west = "west"
    }
    

    等价于:

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

    再比如:

    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) // 1
    print(Season.summer.rawValue) // 2
    print(Season.autumn.rawValue) // 4
    print(Season.winter.rawValue) // 5
    

    枚举递归(Recursive Enumeration)

    实例1: 算术表达式

    indirect enum ArithExpr {
        case number(Int)
        case sum(ArithExpr, ArithExpr)
        case difference(ArithExpr, ArithExpr)
    }
    

    或者这样定义

    enum ArithExpr {
        case number(Int)
        indirect  case sum(ArithExpr, ArithExpr)
        indirect  case difference(ArithExpr, ArithExpr)
    }
    

    使用如下:

    let five = ArithExpr.number(5)
    let four = ArithExpr.number(4)
    let two = ArithExpr.number(2)
    let sum = ArithExpr.sum(five, four)
    let difference = ArithExpr.difference(sum, two)
    
    func calculate(_ expr: ArithExpr) -> Int {
        switch expr {
        case let .number(value):
            return value
        case let .sum(left, right)
            return calculate(left) + calculate(right)
        case let .difference(left, right)
            return difference(left) - difference(right)
        }
    }
    calculate(difference)
    

    MemoryLayout

    可以使用MemoryLayout获取数据类型占用的内存大小(在OC里面有个sizeOf)

    MemoryLayout是支持泛型的.
    实例1

    enum Password {
       case number(Int, Int, Int, Int)
       case other
    }
    var pwd = Password.number(1,2,3,4) // 占用多少个字节?
    pwd = .other // 这个呢?
    
    MemoryLayout<Password>.stride//40, 分配占用的空间大小 
    MemoryLayout<Password>.size//33, 实际用到的空间大小
    MemoryLayout<Password>.alignment//8
    
    enum Season {
      case spring, summer, autumn, winter
    }
    MemoryLayout<Season>.stride//1
    MemoryLayout<Season>.size//1
    MemoryLayout<Season>.alignment//1
    
    var age = 10
    MemoryLayout.stride(ofValue: age)// 8
    MemoryLayout.size(ofValue: age) // 8
    MemoryLayout.alignment(ofValue: age)// 8
    

    这种类型的枚举

    enum Season {
        case spring, summer, autumn, winter
    }
    var s = Season.spring// 1个字节
    
    //原始值是Int
    enum Season : Int {
        case spring = 1, summer = 2, autumn = 3, winter = 4
    }
    var s = Season.spring// 1个字节
    var s1 = Season.summer// 1个字节
    var s2 = Season.autumn// 1个字节
    
    //原始值是String
    enum Season : String {
        //序号 0 1 2 3
        case spring = "1", summer = "2", autumn = "3", winter = "3"
    }
    var s = Season.spring// 1个字节
    var s1 = Season.summer// 1个字节
    var s2 = Season.autumn// 1个字节
    

    思考1:Apple对原始值是怎么实现的?
    思考2:上面例子Password实际用到的空间大小为什么实33?32不行吗?

    关联值、原始值的区别?
    关联值把传进来的关联的值存放到枚举变量里面
    关联值的特点:可以传入不同的值,意味着每一个枚举变量都要有内存来存储不同的值.

    var pwd1 = Password.number(10,999,10000,10000)
    

    原始值和枚举成员绑定在一起,固定不变的一个值.所以没有必要给它分配内存空间放到枚举变量里面

    var s = Season.spring// 1个字节
    var s1 = Season.summer// 1个字节
    var s2 = Season.autumn// 1个字节
    

    猜测:默认值可以这样实现

    enum Season3: Int {
        case spring = 1, summer, autumn, winter
        func rawValue() {
            if self == .spring return 1
           else if self == .summer return 2
           else if self == .autumn return 3
            ...
        }
    }
    

    可选项(Optional)

    类型后面加?

    var name: String? = "Jack"
    name = nil
    
    var age: Int? // 默认值为nil
    age = 10
    age = nil
    

    等价

    var age: Int?
    var age: Int? = nil
    

    不等价

    var age: Int
    var age: Int = 0
    

    强制解包(Forced Unwarpping)

    可选项是对其他类型的包装

    var age1: Int?// 把Int类型的数据添加到盒子里面
    age1 = 10
    var age2 = age1! + 2
    

    对nil强制解包会carsh

    实例

    var num = "abc123"
    var numInt = Int(num)
    if numInt != nil {
        print("字符串转换成功\(numInt!)")
    } else {
        print("字符串转换失败")
    }
    

    可选项绑定(Optional Binding)

    实例1

    if let number = Int("123") {
        print("字符串转换成功\(numInt!)")
    } else {
        print("字符串转换失败")
    }
    

    实例2

    if let season = Season(rawValue: 6) {
    } else {}
    

    while循环中使用可选项绑定

    let strs = ["10","10","abc","5","10","10",]
    var index = 0
    var sum = 0
    while let num = Int(strs[index]), num > 0 {
        sum+=num
        index+=1
    }
    

    空合并运算符??

    源码如下

    func ?? <T>(optional: T?, defaultValue: @autoclosure() throws -> T?) throws -> T?
    func ?? <T>(optional: T?, defaultValue: @autoclosure() throws -> T) throws -> T
    

    a??b
    1、a是可选项
    2、b是可选项或者不是可选项
    3、b跟a存储类型必须相同
    4、如果a不为nil,返回a;如果a为nil,返回b
    5、如果b不是可选项,返回a时会自动解包,
    实例1

    let a: Int? = 1
    let b: Int? = 2
    let c = a ?? b // c是Int?, Optional(1)
    

    实例2

    let a: Int? = nil
    let b: Int? = 2
    let c = a ?? b // c是Int?, Optional(2)
    

    实例3

    let a: Int? = nil
    let b: Int? = nil
    let c = a ?? b // c是Int?, nil
    

    实例4

    var a: Int? = nil
    var b:Int = 1
    let c = a ?? b // c是Int,  1
    

    使用场景:

    var a: Int? = nil
    var b:Int = 2
    // 如果不使用??运算符
    let c: Int
    if let temp = a {
      c = temp
    } else {
      c = b
    }
    

    多个??一起使用

    实例1

    var a: Int? = 1
    var b: Int? = 2
    let c = a ?? b ?? 3 // c是Int, 1
    

    实例2

    var a: Int? = nil
    var b: Int? = 2
    let c = a ?? b ?? 3 // c是Int, 2
    

    实例3

    var a: Int? = nil
    var b: Int? = nil
    let c = a ?? b ?? 3 // c是Int, 3
    

    注意⚠️

    var a: Int? = nil
    var b: Int? = nil
    let c = a ?? 2 ?? b // 这样写是会警告的,想想为什么?
    

    ??跟if let配合使用

    var a: Int? = nil
    var b: Int? = 2
    if let c = a ?? b {
       print(c)
    }
    

    类似于: if a!=nil || b != nil
    使用场景: 如果有多个可选项,其中有一个可选项不为nil,就可以进来.

    var a: Int? = nil
    var b: Int? = 2
    if let c = a, let d = b {
       print(c)
    }
    

    类似于: if a!=nil && b != nil

    if 语句实现登录

    看例子前先补充一下知识点:

    • swift里面通过key从字典里面取出来的value是可选类型的.
    • 数组里面通过下标取出来的是真实类型(所以数组是否越界需要我们自己判断呀)
    func login(_ info: [String: String]) {
        let userName: String
        if let temp = info["username"] {
            userName = temp
        } else {
            print("请输入用户名")
            return
        }
        let pwd: String
        if let temp = info["password"] {
            pwd = temp
        } else {
            print("请输入密码")
            return
        }
    
        print("用户名:\(userName),密码:\(pwd)登录中")
    }
    

    guard语句

    当guard语句的条件为false时,就会执行大括号里面的代码;
    当guard语句的条件为true时,就会跳出guard语句;
    guard语句特别适合用来提前退出.

    guard 条件 else {
    // do something
    退出当前作用域
    // return、break 、continue 、throw error
    }
    

    当guard语句进行可选绑定时,绑定的常量(let)、变量(var)也能在外层作用域使用

    func login1(_ info: [String: String]) {
       guard let userName = info["username"] else {
           print("请输入用户名")
           return
       }
       guard let pwd = info["password"] else {
           print("请输入密码")
           return
       }
    
       print("用户名:\(userName),密码:\(pwd)登录中")
    }
    

    隐私解包(Implicitly Unwrapped Optional)

    在某些情况下,可选值一旦被设定值之后,就会一直拥有值,
    在这种情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值.
    可以在类型后面加一个感叹号!定义一个隐式解包的可选项
    以前写法

    let num1: Int? = 10
    let num2: Int = num1!
    

    隐私解包的可选项

    var num1: Int! = 10
    var num2: Int = num1
    

    无论是Int!还是Int?都是可选类型

    var num1: Int! = 10
    var num2: Int = num1
    if num1 != nil {
      print(num1 + 6)
    }
    if let num3 = num1 {
      print(num3)
    }
    
    let num1: Int! = nil
    //Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
    let num2: Int = num1
    

    感觉这种类型没啥用?有木有

    使用场景:如果能确保一定有值可以使用
    希望别人给你值,不希望别人给你nil,但是别人可能给你的是nil.

    多重可选项

    var num1: Int?
    var num2: Int?? = num1
    var num3: Int?? = 10

    frame variable -R

    简写:fr v -R 变量名 查看变量结构

    相关文章

      网友评论

          本文标题:枚举可选型

          本文链接:https://www.haomeiwen.com/subject/vxkerktx.html