Swift可选类型、隐式可选类型与可选链

Swift 可选类型、隐式可选类型与可选链

在 OC 中 nil 表示一个指向不存在的对象的指针,而 Swift 中 nil 不是指针,而是一个确定的值,它表示一切值缺失的情况,所谓值缺失就是一个和 Swift 的可选类型相关的概念。

可选类型 optional

可选类型的变量可以有值,也可以无值,无值的变量取值得到的就是 nil,相对应的,非可选类型的变量必须有值,也不能赋值为 nil,如果没有值就会报编译期错误。Swift 声明一个变量时默认情况下就是非可选的,即必须给这个变量赋值一个非空值。之所以引入可选类型和非可选类型是为了增强便一阶段的安全检查,对于因 nil 引发的崩溃可以在编译期就检查出来,避免崩溃发现时间延时到运行时。

声明一个可选类型变量和不可选类型变量如下

    var optionalVariable:String? = nil
    var nonOptionalVariable:String = "Yasic"

当然可选类型变量默认不赋值的时候就是 nil,并且对于一个类中的非可选类型变量,也可以将赋值操作延时到初始化 init 方法 中。

那么对于非可选类型参数就可以安全取值安全使用了,而可选类型参数则需要在使用前检查变量是否有值,通常我们会这样检查

        if optionalVariable != nil {
            optionalVariable!.append("test")
        }

其中用到了感叹号运算符,它表示对可选值的强制解析,表明开发者明确知道这个可选类型变量在此处是有值的所以强制从中取值,但是如果变量中的确没有值就会引发运行时崩溃了。

所以强制解析功能应当谨慎使用,Swift 更推荐的方式是可选绑定(Optional Binding),与强制解析不同,可选绑定会判断可选类型是否有值,如果有值就将其赋值给一个临时常量或变量,可选绑定可以用在 if 和 while 语句中来对可选类型的值进行判断后赋值给临时常量或变量。

        if let tempVariable = self.optionalVariable {
            print(tempVariable)
        }

临时变量也可以是 var 型的,从而可以在分支中改变它的值。在分支中使用到临时变量时能保证它一定值,从而就避免了强制解析的过程。

隐式可选类型

可选类型变量增强了代码的安全性,但是每次使用到可选类型变量都需要加判断或者强制解析比较麻烦,这时可以利用隐式可选类型。隐式可选类型实际就是一个普通的可选类型,但是开发者明确知道此可选类型变量在第一次赋值后一定总有值,从而用隐式可选类型省却了强制解析的过程。

声明一个隐式可选类型的方式

var hiddenOptinalVariable:String! = nil

使用时可以直接取值,此时表明开发者能明确保证可以取到值,但是如果的确没有值,就会引发运行时崩溃。

hiddenOptinalVariable.append("yasic")

当然也可以用上面的方法对隐式可选类型进行判空

        if var temp = self.hiddenOptinalVariable {
            temp.append("yasic")
        }

可选类型的应用

空合运算符 Nil Coalescing Operator

空合运算符 "??" 是对三目运算符的简化,针对可选类型变量 a 进行空判断,若非空则返回 a 的变量值,否则返回空合运算符后的默认值 b。它的使用有两个条件

  • a 必须是可选类型
  • b 的类型必须与 a 一致
        var temp = self.hiddenOptinalVariable ?? "123"

方法返回可选类型值

可选类型变量可以出现在方法的返回值当中,表明此方法的返回值能为 nil

    func returnOptinalVariable(input:Bool) -> String? {
        if !input {
            return nil
        } else {
            return "true"
        }
    }

类返回可选类型对象

对于 Swift 的类有一个特殊的方法,构造方法 init,这个方法也可以返回一个可选类型的值,它的主要意义在于能够根据初始化时传入的值以及环境参数来判断是否可以返回一个实例对象,通常也称这类构造方法为可失败构造器。

    convenience init?(input:Bool){
        if !input {
            return nil
        }
        self.init()
    }

闭包返回可选类型值

闭包返回可选类型闭包,表明此闭包可能为 nil

    func returnOptionalBlock(value: Bool) -> (() -> (Void))? {
        if value {
            return { () in
                print("可选闭包")
            }
        } else {
            return nil
        }
    }
    
    if let voidBlock = self.returnOptionalBlock(value: false) {
        voidBlock()
    }

要注意对于为 nil 的闭包进行强制解析执行会发生运行时错误 "Fatal error: Unexpectedly found nil while unwrapping an Optional value"。

结构体和枚举的可失败构造器

类似类的可失败构造器,结构体的可失败构造器也是支持的

enum OperationError:Error {
    case ErrorFirst
    case ErrorSecond
    case ErrorThird
    init?(type:String) {
        switch type {
        case "first":
            self = .ErrorFirst
        case "second":
            self = .ErrorSecond
        case "third":
            self = .ErrorThird
        default:
            return nil
        }
    }
}

struct PersonStruct {
    let name: String
    init?(name: String) { // 可失败构造器
        if name.isEmpty { return nil } // 如果实例化为空串,则返回nil(即实例化失败)
        self.name = name
    }
}

错误处理中的可选类型

对于可能抛出错误的方法调用时应当使用 try 语法来捕捉错误,当然有时候并不对捕捉到的错误进行处理,此时代码可能会写成这个样子

let picture = try! loadImage(atPath: "...")

这样其实是禁止了错误的抛出,一旦有错误时就会崩溃,所以建议的语法如下

let picture = try? loadImage(atPath: "...")

类型转换中的可选类型

对于某种类型的变量进行向上转换时不会出现失败的情况,但是向下转型就可能会失败

        let yasic = Person(name: "Yasic")
        let yasicCopy = yasic as Male

这样做会有编译期报错 'Person' is not convertible to 'Male'; did you mean to use 'as!' to force downcast?

此时建议使用可选绑定来进行类型向下转换

        let yasic = Person(name: "Yasic")
        if let yasicCopy = yasic as? Male {
            
        }

可选链 Optional Chaining

Swift 对可选链的定义如下

可选链是一种可以请求和调用属性、方法及下标脚本的过程,它的可选性体现在请求或调用的目标当前可能为空 nil。如果可选的目标有值,那么调用就会成功;相反,如果可选的目标为 nil,那么调用就会返回 nil。多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空 nil 将导致整个链失效。

此处假设有两个类 Person 和 Room

class Person {
    var name:String?
    var room:Room?
    init(name:String) {
        self.name = name
    }
    
    func nameStr() -> String? {
        if let name = self.name {
           return name
        }
        return nil
    }
}

class Room {
    var roomAddr:String?
    var roomArea:Int
    var people:[String]?
    init(roomAddr:String, roomArea:Int) {
        self.roomAddr = roomAddr
        self.roomArea = roomArea
    }
    
    func roomAddrStr() -> String? {
        if let addr = self.roomAddr {
            return addr
        }
        return nil
    }
}

可以看到,Person 有一个可选类型的成员变量 room,表示它可能为 nil,也可能有值,还有一个返回可选类型值的方法 nameStr,Room 类也有一个返回可选类型值的方法 roomAddrStr。

下面是一些具体的使用

  • 可选链调用属性
        if let area = yasic.room?.roomArea {
            
        }

这样获取的就是一个 Int? 类型的值,而不是 roomArea 所定义的 Int 类型。

  • 可选链调用方法
        if let result = yasic.room?.roomAddrStr() {
            
        }
  • 可选链访问数组下标
        if let result = yasic.room?.people?[1] {
            
        }

需要注意的是可选链始终返回的是可选类型变量,不会因为最后一层的变量是非可选类型变量就返回非可选类型变量,并且可选链中任意一节失败就会直接返回 nil。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容