美文网首页
Swift可选类型知识梳理

Swift可选类型知识梳理

作者: _小沫 | 来源:发表于2019-11-09 23:57 被阅读0次

    Swift中加入了可选类型Optional,用来处理值可能缺失的情况。可选类型表示两种可能: 或者有值, 你可以解析并访问这个值, 或者根本没有值;

    可选类型的作用

    • 处理哨岗值问题
      Optional的存在是很有必要的,大部分语言是没有这种类型的,它们表示值可能缺失的情况就不太方便了;比如说一个Int类型的属性,如何表示这个属性未赋值过或者说缺失为空的情况呢;Int这类基本类型并不能像其他对象一样通过nil(null)来表示缺失情况,同样也不好使用默认值0来表示,那么一般的处理方式就是设置一个哨岗值来表示:我们可以约定这个Int类型的属性默认/缺失值为-1,这个-1就是哨岗值;OC中的 NSNotFound其实也是类似的哨岗值,只不过这个值是个比较大的数;
    static const NSInteger NSNotFound = NSIntegerMax;
    #define NSIntegerMax    LONG_MAX
    

    不过这种策略是有问题的,因为这个哨岗值不管从哪个角度看都是一个真实值;你也可能会忘记检查这个值而不小心使用了它;而可选类型很好的处理了这种情况;

    • 类型安全
      在OC中,对nil发送消息是安全的,但在Swift中是有问题的;编写代码时,开发者都不能明确某个对象在某个时刻会变为nil;如果再去使用这个对象,就可能会造成异常;有了可选类型,开发者在使用对象时就必须去解析这个值并判断为nil的情况;

    可选类型的本质

    Optional结构如下:

    public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    
        /// The absence of a value.
        ///
        /// In code, the absence of a value is typically written using the `nil`
        /// literal rather than the explicit `.none` enumeration case.
        case none
    
        /// The presence of a value, stored as `Wrapped`.
        case some(Wrapped)
    
        ....
    }
    
    • 可以看到,它本质是一个enum类型,而且是一个关联值的enum;这个枚举只有两个值,表示缺少值的none,表示具体关联值的some(Wrapped),Wrapped就是具体值;
      获取关联值的唯一方法就是使用switch或者if case语句;和哨岗值不同,除非通过解包取出关联值,否则是不可能意外的得到这个值;
    • 另外Optional<Wrapped>这里也是一个泛型,原则上任何类型都有可选类型;
    • Optional 同时也遵循了ExpressibleByNilLiteral协议,ExpressibleByNilLiteral这个协议就是nil字面量协议,类似的还有字符串的字面量协议ExpressibleByStringLiteral,数组字面量协议ExpressibleByArrayLiteral;这也是case none就能以字面量nil表示的原因;

    我们可以这样声明一个可选整型:

    var opValue: Optional<Int> = Optional.init(1)
    // or
    // var opValue: Optional<Int> = 1
    

    这个声明可以用?语法糖简化表示:

    var opValue: Int? = 1
    

    使用switch解包得到关联值:

    switch opValue {
    case .none:
        print("nil")
    case .some(let value):
        print(value)
    }
    

    或使用if case语句:

    if case Optional.some(let value) = opValue {
        print(value)
    }else {
        print("nil")
    }
    

    因为遵循了字面量协议上面的解包可以替换为:

    switch opValue {
    case nil:
        print("nil")
    case .some(let value):
        print(value)
    }
    

    Swift 的 nil 和 Objective-C 中的 nil 并不一样。在 Objective-C 中,nil 是一个指向不存在对象的指针。在 Swift 中,nil 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 nil,不只是对象类型。

    强制解析

    可以使用 if 语句和 nil 比较来判断一个可选值是否包含值;
    当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号!来获取值。这个惊叹号表示“我知道这个可选有值,请使用它”,这就是可选值的强制解析;

    if opValue != nil {
        print(opValue!)
    }else {
        print("nil")
    }
    

    使用 ! 来获取一个不存在的可选值会导致运行时错误。使用 ! 来强制解析值之前,一定要确定可选包含一个非 nil 的值。

    可选绑定

    • if let
      可选类型很常用,很多时候都需要使用上面的switch,if case解析;这种解析的确有点冗余;不过使用if let进行可选绑定(optional binding)来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量,可以更简单的解析;
      上面例子使用可选绑定的代码:
    if let value = opValue {
        print(value)
    }
    

    也可以在同一个if语句中绑定多个值,当多个可选值都有解析值时条件成立:
    只有opValue1,opValue2都有值时才会调用输出结果:

    let opValue1: Int? = 1
    let opValue2: Int? = nil
    if let v1 = opValue1, let v2 = opValue2 {
        print(v1,v2)
    }
    
    • guard let
      通常使用if let判断有值之后,会做具体的逻辑实现,通常代码多,而且也多了一层分支;guard let和if let刚好相反,guard let守护一定有值。如果没有,直接返回;guard let 在一定程度上能使代码更加清晰易读:
        guard let v = opValue else { return }
        print(v)
    

    一个比较明显的例子:

    if let v1 = opValue1 {
        if let v2 = opValue2 {
            if let v3 = opValue3 {
                print(v1,v2,v3)
            }
        }
    }
    
    guard let v1 = opValue1 else { return }
    guard let v2 = opValue2 else { return }
    guard let v3 = opValue3 else { return }
    print(v1,v2,v3)
    

    guard let同样可以绑定多个值:

    guard let v1 = opValue1,let v2 = opValue2,let v3 = opValue3 else { return }
    print(v1,v2,v3)
    

    if 条件语句中使用常量和变量来创建一个可选绑定,仅在 if 语句的句中(body)中才能获取到值。相反,在 guard 语句中使用常量和变量来创建一个可选绑定,仅在 guard 语句外且在语句后才能获取到值;

    另外,guard并不局限于绑定上,它也能够接受任何在if语句中能接受的条件;附:使用 guard 的正确姿势

    可选链

    可选链Optional Chaining:多个连续的调用可以被链接在一起形成一个调用链,如果其中任何一个节点为空(nil)将导致整个链调用失败。也就是说可以将可选值的调用链接起来;

    let str: String? = ""
    str?.uppercased().lowercased()
    
    • 在OC中,对nil发送消息什么都不会发生;在Swift中,可以通过可选链来达到同样的效果;
    delegate?.callback()
    

    和OC不同的是,如果没有值,那么这里的问号对于代码的阅读者来说是一个清晰的信号,表示方法可能不会调用;

    • 当调用可选链得到一个返回值时,这个返回值也是可选类型;因为可选链的结果可能为nil;
    • 可以使用可选链式调用代替强制解析,使用可选链当可选值为空时调用只会调用失败,然而强制解析将会触发运行时错误。
    • 可选链简化判断:
    var str: String? = ""
    if str != nil {
        str = str?.lowercased()
        if str != nil {
            str = str?.uppercased()
            print(str)
        }
    }
    
    if let result = str?.lowercased().uppercased() {
        print(result)
    }
    

    隐式解析

    有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。这种情况我们就可以定义隐式解析可选类型(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(String?)改成感叹号(String!)来声明一个隐式解析可选类型。

    let opValue: Int! = 1
    print(opValue + 1)
    

    如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。

    相关文章

      网友评论

          本文标题:Swift可选类型知识梳理

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