美文网首页
枚举 可选项

枚举 可选项

作者: 纳兰沫 | 来源:发表于2019-08-12 13:20 被阅读0次

    枚举

    枚举的基本用法

    关联值

    如果取值只有几个固定的类型 可以考虑使用枚举

    有时会将`枚举的成员值`跟`其他类型的值`关联存储在一起
    
    enum Score {
        case points(Int)
        case grade(Character)
    }
    var score = Score.points(96)
    score = .grade("A")
    
    switch score {
    case let .points(i):
        print(i,"poin")
    case let .grade(i):
        print(i,"point")
    }
    
    enum Date {
    case digit(year: Int, month: Int, day: Int)
    case string(String)
    }
    var date = Date. digit(year: 2011, month: 9, day: 10)
    date = .string("2011-09-10")
    switch date {
    case .digit(let year, let month, let day):
    print(year, month, day)
    case let .string(value):
    print (value)
    }
    

    原始值

    枚举成员可以使用"相同类型"的默认值预先关联 这个就是原始值
    
    enum PokerSuit: Character {
        
        case spade = "1"
        case heart = "2"
        case diamond = "3"
        case club = "4"
    }
    

    Character 是原始值的类型

    隐式原始值

    如果枚举的原始值类型是Int String  Swift会自动分配原始值
    原始值会跟成员的名称一样
    

    递归枚举

    递归枚举必须加 indirect 关键字 否则会报错

    indirect enum ArithExpr {
        case number(Int)
        case sum(ArithExpr,ArithExpr)
        case difference(ArithExpr,ArithExpr,ArithExpr)
    }
    
    递归枚举的具体实现.png

    MemoryLayout

    可以使用MemoryLayout获取数据类型占有的内存大小
    
    MemoryLayout<Int>.size                     变量实际用到的空间大小
    MemoryLayout<Int>.stride                   变量分配占有的内存大小
    MemoryLayout<Int>.alignment                对齐参数
    
    enum Password {
        case number(Int, Int, Int, Int)
        case other
    }
    "是32 + 1的字节总数 那个1 是为了判断到底是number 还是 other"
    
    var pwd = Password.number(5, 6, 4, 7)
    pwd = .other
    MemoryLayout.size(ofValue: pwd)
    
    MemoryLayout.size(ofValue: pwd)
    MemoryLayout.stride(ofValue: pwd)
    MemoryLayout.alignment(ofValue: pwd)
    
    

    关联值和原始值的区别
    1.原始值是不会存储到枚举变量里面的 不会占有他们的内存 只占有1个字节
    2.关联值是会存储到枚举变量里面的 是占用枚举变量的内存的,然后,会根据不同的值 占有不同的内存

    原始值不考虑存储在什么位置 因为可以直接在枚举里面写rawValue这个方法根据if判断来实现

    窥探内存

    1.png
    2.png
    枚举类型有关联值的话 首先必定有一个字节来存储成员值 N个字节存储关联值(N取占用内存最大的关联值),然后 任何一个case的关联值都共用这 N个字节
    enum TestEnum {
    case test1(Int, Int, Int)
    case test2(Int, Int)
    case test3(Int)
    case test4 (Bool)
    case test5
    }
    print(MemoryLayout<TestEnum>.size)
    print(MemoryLayout<TestEnum>.stride)
    print(MemoryLayout<TestEnum>.alignment)
    25 32 8
    

    25个字节 24个是用来存放原始值的内容的 第25个字节是存放是枚举成员的类型

    enum TestEnum{
        case test
    }
    var t = TestEnum.test
    
    print(MemoryLayout<TestEnum>.size)
    print(MemoryLayout<TestEnum>.stride)
    print(MemoryLayout<TestEnum>.alignment)
    0  1  1
    

    枚举里面只有一个case 不需要内存区分是哪一个 也就不需要那一个字节

    enum TestEnum{
        case test(Int)
    }
    var t = TestEnum.test(10)
    
    print(MemoryLayout<TestEnum>.size)
    print(MemoryLayout<TestEnum>.stride)
    print(MemoryLayout<TestEnum>.alignment)
    8 8 8
    

    可选项

    可选项 一般也可叫可选类型 它允许将值设置为nil
    
    在类型名称后面加一个问号? 来定义一个可选项
    
    var array = [1,15,40,29]
    func get(_ index: Int) -> Int? {
    
        if index < 0 || index >= array.count{
            return nil
        }
        return array[index]
    }
    
    get(3)
    

    强制解包

    1.可选项是对其他类型一层包装  可以理解为一个盒子
        - 如果为nil  那么它是一个空盒子
        - 如果不为nil  那么盒子里面装的是  被包装类型的数据
    2.如果要从可选项中取出被包装的数据(将盒子里面装的东西取出来),需要使用感叹号! 进行强制解包
    3.如果对值为nil的可选项(空盒子)进行强制解包 将会产生运行时错误
    

    强制解包 只是把值拿出来用一下,盒子里面还是有内容的,不是空的

    判断可选项里面是否包含值

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

    可选项绑定

    1.可以使用可选项绑定来判断可选项是否包含值
        - 如果包含就自动解包  把值赋值给一个临时的常量或者变量 并返回true 否则返回false
    
    if let number = Int("123"){
        print("字符串转换成功");
       //number作用域仅限于这个大括号
       //number 是强制解包之后的Int值
    }else{
        print("字符串转换失败");
    }
    

    等价写法

    if let first = Int("4") {
        if let second = Int("42") {
            if first < second && second < 100 {
                print("\(first) < \(second) < 100")
            }
        }
    }
    
    if let first = Int("4"),
        let second = Int("42"),
        first < second && second < 100{
        print("\(first) < \(second) < 100")
    }
    

    可选项绑定没办法使用&& 只能使用,代表同时成立

    while 循环中使用可选项

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

    空合并运算符??(Nil - Coalescing)

    public func ?? <T>(optional: T? defaultValue: @autoclosure() throws -> T?) rethrows -> T?
    public func ?? <T>(optional: T? defaultValue: @autoclosure() throws -> T) rethrows -> T
    
    a ?? b
    - a 是可选项 
    - b 可以是可选项 或者不是可选项
    - b 跟 a 的存储类型必须相同
    - 如果a 不为nil  就返回a
    - 如果a 为nil  就返回b
    - 如果b不是可选项  返回a的时候会自动解包
    
    let a: Int? = 1
    let b: Int? = 2
    let c = a ?? b    optinon(1)
    
    let a: Int? = nil
    let b: Int? = 2
    let c = a ?? b  optinon(2)
    
    let a: Int? = 1
    let b: Int = 2
    let c = a ?? b  1
    

    返回类型取决于b

    //如果不使用?? 运算符
    let a: Int? = nil
    let b: Int = 2
    let c: Int
    if let tmp = a {
        c = tmp
    }else{
        c = b
    }
    
    多个 ?? 一起使用
    let a: Int? = 1
    let b: Int? = 2
    let c = a ?? b ?? 3
    print("\(c)")  1
    

    多个 ?? 一起使用 看最右边的类型

    ?? 跟 if let 配合使用
    let a: Int? = nil
    let b: Int? = 2
    if let c = a ?? b {
        print(c)
    }
    
    let a: Int? = nil
    let b: Int? = 2
    if let c = a, let d = b {
         print(c)
         print(d)
    }
    

    if语句实现登录

    func login(_ info: [String : String]) {
        let username: String
        if let tmp = info["username"]{
            username = tmp
        }else{
            print("请输入用户名")
            return
        }
        let password: String
        if let tmp = info["password"] {
            password = tmp
        }else{
            print("请输入密码")
            return
        }
        print("用户名 \(username)","密码: \(password)")
    }
    
    login(["username" : "jack", "password" : "123456"])
    login(["password" : "123456"])
    login(["username" : "jack"])
    
    用户名 jack 密码: 123456
    请输入用户名
    请输入密码
    

    数组下标越界 会直接崩溃 而字典没有相关key的话,会返回nil 可以使用if let判断

    guard语句

    guard 条件 lese{
          //do something
         //退出当前作用域 return break continue throw error
    }
    
    当guard 条件为false的时候 会指向大括号里面的代码
    当guard 条件为true的时候  就会跳出guard语句
    guard语句特别适合用于"提前退出"
    
    - 当guard语句进行可选项绑定的时候 绑定的常量 变量也能在外层作用域中使用
    
    func login(_ info: [String : String]) {
        guard let username = info["username"] else{
            print("请输入用户名")
            return
        }
        guard let password = info["password"] else{
            print("请输入密码")
            return
        }
        print("用户名 \(username)","密码: \(password)")
    }
    
    login(["username" : "jack", "password" : "123456"])
    login(["password" : "123456"])
    login(["username" : "jack"])
    
    用户名 jack 密码: 123456
    请输入用户名
    请输入密码
    

    隐式解包

    1.在某些情况下 可选项一旦被设置值之后,就会一直拥有值
    2.在这种情况下 可以去掉检查 也不必每次访问的时候都进行解包 因为他能确保每次访问的时候都有值
    3.可以在类型后面加个感叹号! 定义一个隐式解包的可选项
    
    let num1: Int! = 10 //无论是感叹号还是问号 都是可选项
    let num2: Int = num1//隐式解包的可选项 可以省略感叹号
    if num1 != nil {
        print(num1 + 6)
    }
    if let num3 = num1 {
        print(num3)
    }
    

    隐式解包的可选项 如果赋值为nil 会报错
    如果希望别人给你具体的值 但是,有可能还是返回nil 就可以使用隐式解包 如果返回为空 那么就直接报错 提醒他不合理
    无论是感叹号还是问号 都是可选项

    字符串插值

    - 可选项在字符串插值或者直接打印时 编译器直接发生警告
    
    var age: Int? = 10
    print("my age is \(age)")
    
    具体警告.png
    至少3种方法解决警告
    1.强制解包 !
    2.String(describing: age)
    3.age ?? 0
    

    多重可选项

    var num: Int? = 10       //可选项 盒子里面装了10
    var num2: Int?? = num1  //大盒子里面 装了一个可选项 这个小盒子里面装了一个10
    var num3: Int?? = 10    //大盒子里面 装了一个可选项 这个小盒子里面装了一个10
    

    可以使用ldb指令 frame variable -R 或者fr v -R 查看区别

    列子.png
    num1 和 num3 不相同 类型不同

    相关文章

      网友评论

          本文标题:枚举 可选项

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