美文网首页Swift Swift底层原理分析
Swift底层原理探索3----可选项

Swift底层原理探索3----可选项

作者: RUNNING_NIUER | 来源:发表于2020-04-13 15:37 被阅读0次

    可选项(Optional)

    • 可选项,一般也叫做可选类型,它允许将值设置为nil
    • 在类型名称后面加个问号?来定义一个可选项
    var name: String = "Jack"
    name = nil
    
    var age: Int?
    age = 10
    age = nil
    

    C语言中,变量声明之后,会自动被赋予初始值,Swift则不会这样,但是Swift的Optional则会有默认的初始值nil

    func test() {
        var age: Int -->没有初始值,必须赋值之后才能使用
        var weight: Double?  --> 默认初始值为nil, 等价于  var weight: Double? = nil
    }
    

    使用场景,有可能需要使用nil的地方

    var array = [1,13,45,33]
    func get(_ index: Int) -> Int? {
        if index < 0 || index >= array.count {
            return nil
        }
        return array[index]
    }
    print(get(1))// Optional(15)
    print(get(-1))// nil
    print(get(5)) // nil
    

    强制解包(Forced Unwrapping)

    • 可选项是对其他类型的一层包装,可以将它理解成一个盒子
      1. 如果为nil,那么它是个空盒子
      2. 如果不为nil,那么盒子里装的是:被包装类型的数据
    var age: Int? // 默认就是nil
    age = 10
    age = nil
    
    image
    • 如果要从可选项中取出被包装的数据(将盒子里装的东西取出来),需要使用感叹号!进行强制解包
    var speed: Int? = 10
    var speedInt: Int = speed!
    speedInt += 10
    
    • 如果对值为nil的可选项(空盒子)进行强制解包,将会产生运行时错误
    var age: Int?
    age!
    
    运行报错:Fatal error: Unexpectedly found nil while unwrapping an Optional value: file Optional.xcplaygroundpage
    

    判断可选项是否包含值

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

    可选项绑定(Optional Binding)

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

    用在枚举值中

    enum Season: Int {
        case spring = 1, summer, autumn, winter
    }
    if let season = Season(rawValue: 6) {  -->有可能得到对应的case, 有可能得不到,那么结果就为空
        switch season {
        case .spring:
            print("the season is spring")
        default:
            print("the season is other")
        }
    }  else {
           print("no such season")
    }
    

    注意下面的等价用法

    if let first = Int("4") {
        if let second = Int("42") {
            if first < second && second < 100 {
                print("\(first) < \(second) < 100")
            }
        }
    }
    
    ======等价写法如下, 通过逗号来分割 带可选项绑定的 if判断条件
    
    if let first = Int("4"),
        let second = Int("42"),
        first < second && second < 100 {
        print("\(first) < \(second) < 100")
    }
    

    while循环中使用可选项绑定

    ---->遍历数组,将画遇到的整数都加起来,如果遇到负数或者非数字,停止便利
    var strs = ["10", "20", "abc", "-30", "30"]
    var index = 0
    var sum = 0
    while let num = Int(strs[index]), num > 0 {
        sum += num
        index += 1
    }
    print(sum) // 30
    

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

    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是可选项 或者 不是可选项
    • ba的存储类型必须相同
    • 如果a不为nil,就返回a
    • 如果anil,就返回b
    • 如果b不是可选项,返回a时回自动解包
    let a: Int? = 1
    let b: Int? = 2
    let c = a ?? b //c是Int?, Optional(1)
    
    
    let a: Int? = nil
    let b: Int? = 2
    let c = a ?? b //c是Int?, Optional(2)
    
    let a: Int? = nil
    let b: Int? = nil
    let c = a ?? b //c是Int?, nil
    
    let a: Int? = 1
    let b: Int = 2
    let c = a ?? b //c是Int, 1
    
    let a: Int? = nil
    let b: Int = 2
    let c = a ?? b //c是Int, 2
    

    如果没有??,有些代码写起来就比较麻烦

    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 //c是Int, 1
    
    let a: Int? = nil
    let b: Int? = 2
    let c = a ?? b ?? 3 //c是Int, 1
    
    let a: Int? = nil
    let b: Int? = nil
    let c = a ?? b ?? 3 //c是Int, 1
    

    ??跟if let配合使用

    let a: Int? = nil
    let b: Int? = 2
    if let c = a ?? b {
        print(c)
    }
    -->类似于 if a != nil || b != nil {}
    
    if let c = a, let d = b {
        print(c)
        print(d)
    }
    -->类似于 if a != nil && b != nil {}
    

    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)", "登录ing")
    }
    
    login(["username": "jack", "password": "123456"])
    login(["password": "123456"])
    login(["username": "jack"])
    ************运行结果
    用户名:jack 密码:123456 登录ing
    请输入用户名
    请输入密码
    

    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)", "登录ing")
    }
    
    login(["username": "jack", "password": "123456"])
    login(["password": "123456"])
    login(["username": "jack"])
    ***********运行结果
    用户名:jack 密码:123456 登录ing
    请输入用户名
    请输入密码
    

    隐式解包(Implicitly Unwrapped Optional)

    • 在某些情况下,可选项一旦被设定值之后,就会一直拥有值
    • 在这种情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值
    • 可以在类型后面加个感叹号!定义一个隐式解包的可选项
    let num1: Int! = 10 //如果能确定某个变量会一直不为nil,可以使用这种声明方式,
    let num2: Int = num1 // 可以直接使用,系统会自动解包后赋值,当然也可以使用强制解包方式,但是num1的本质还是一个optional
    if num1 != nil {
        print(num1 + 6)
    }
    
    if let num3 = num1 {
        print(num3)
    }
    
    
    

    如果给隐式解包类型的optional变量赋值nil,当该变量被赋值给其他变量/常量式,会出现 运行时报错

    let num4: Int! = nil
    let num5: Int = num4  -->注意,程序执行完这一句才会报错,在执行这一句的时候,会对num4进行隐式解包,结果发现它内部是nil,因此把nil取出来赋值给  num5: Int 的时候就报错了,因为 num5 不能为空
    Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
    

    上述的案例,让人感觉使用带!的optional存在很多风险,其实用下面的方式莫不是更保险点吗,

    var num5: Int = 10
    let num5: int = num56
    

    这样至少在程序运行之前,就能看到错误,如果有的话。

    实际上,我们大部分场景下,也都是推荐使用带?optional,那用!声明的optional存在的意义是什么呢?

    • 如果你提供一套api给外界使用,并且期望使用者严格遵守你的要求不要传nil过来,并且认为使用者在错误使用的时候而导致程序直接报错崩溃就是你期待的,那么你可以使用这种用法。除此之外,还是不用为妙。

    字符串插值

    可选项在字符串插值或者直接打印的时候,编译器会发出警告

    var age: Int? = 10
    print("My age is \(age)")//直接进行字符串插值,会产生编译器警告
    //⚠️String interpolation produces a debug description for an optional value; did you mean to make this explicit?⚠️
    

    3种常用的方法可以消除警告

    var age: Int? = 10
    print("My age is \(age!)")
    print("My age is \(String(describing: age))")
    print("My age is \(age ?? 0)")
    

    多重可选项

    var num1: Int? = 10
    var num2: Int?? = num1
    var num3: Int?? = 10
    print(num2 == num3) // true
    

    num1num2num3的结构分别如下

    image

    再看如下

    var num1: Int? = nil
    var num2: Int?? = num1
    var num3: Int?? = nil
    print(num2 == num3) // false
    
    (num2 ?? 1) ?? 2  //1
    (num1 ?? 1) ?? 2  //2
    
    image

    可以使用LLDB指令 frame variable -R 或者 fr v -R来查看变量的内部信息。

    有关Swift的可选项就整理到这里。

    相关文章

      网友评论

        本文标题:Swift底层原理探索3----可选项

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