Swift-可空链式调用(Optional Chaining)(十五)

前言

其实可空链式调用并没有它的名字那么陌生,简而言之就是对于可选类型Optional(使用问号 ? 后缀表示)和强制展开类型(使用感叹号 ! 后缀表示)的使用方法。在平常写代码的时候只是大概的清楚哪些值是可空的,哪些值是肯定存在的,但是并没有深究可空的调用有何优点,有何使用时需要注意的事项。至少前面写不少示例代码的时候,我也是大都按照自己的想法去定义的。这一小节就是对可空调用的详细描述,至于链式,就是多层调用,大概就是school.classroom.student.name这样的调用方法。

小节知识点,看着挺多挺长,但是如果把可空链式调用这几个忽略掉,就会发现都是我们已经非常熟悉的基本用法:

  • 使用可空链式调用来强制展开
  • 使用可空链式调用定义模型类
  • 通过可空链式调用访问属性
  • 通过可空链式调用来调用方法
  • 通过可控调用方法来访问下标
  • 多层链接
  • 对返回可空值得函数进行链接

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

Swift的可空链式调用和Objective-C中的消息为空有点像,但是Swift可以使用在任何类型中,并且能够检查调用是否成功。一个可空调用相当于Objective-C中if (message == nil) { } else { },只是Objective-C中只能用在对象或者变量中,远没有Swift中功能强大。

分条详述

  1. 使用可空链式调用来强制展开

    下面这几句话读起来有点拗口,多理解就好:通过在想调用非空的属性、方法、或下标的可空值optional value后面放置一个问号 ? ,可以定义一个可空链。这一点很像在一个可空值后面放一个感叹号 ! 来强制展开其中值。它们的主要区别在于当可空值为空时可空链式只是调用失败,然而强制揭开将会出发运行错误。

    简单来说,就是当调用一个属性或方法时,我们希望它是非空的,是有值的,但是这个我们并不能确定有值,就会用到可空链。譬如获取网络数据,然后展示,我们当然希望是获取到数据的,但是有时候因为网络、后台的问题,数据为空,此时如果用可空链来处理,就会简单些。

    需要强调的是,可空链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了一个可空类型值。例如,当可空链式调用成功时,一个int类型的结果将会返回int?类型。

    下面几行示例代码展示可空链式调用和强制展开的一些不同点:

    //  户主信息
    class Person {
    var residence: Residence?
    }
    //  房子信息,一共有多少房间
    class Residence {
    var numberOfRooms = 1     //  默认只有一个房间
    }
    //  创建一个新的 Person 实例
    let personOne = Person()
    //  由于 residence 属性为空,所以下面如果将 ? 换成 ! 强制解开,会报错。另外,这里代码补全就是带 ? 的。
    print(personOne.residence?.numberOfRooms)     //  输出: nil
    //  初始化 residence 属性
    personOne.residence = Residence()
    //  此时 residence 已经有了初始值,但是胆码补全的时候还是用了 ? ,此时如果将 ? 改为 ! , 并不会报错,因为是对一个已经确定的非空值强制展开
    print(personOne.residence!.numberOfRooms)      //  输出: 1
    print(personOne.residence?.numberOfRooms)      //  输出: Optional(1)  ,  表示可空的

    其实可空链式调用最常用的一个场景就是if语句的判断,当可空链式调用返回是nil时执行操作A,不是nil时执行操作B,如下:

    //  可空链式调用用于 if 语句判断
    if let person = personOne.residence?.numberOfRooms {
    //  此时不为空  personOne.residence?.numberOfRooms = 1
    } else {
    //  此时为空   personOne.residence?.numberOfRooms = nil
    }
  2. 通过可空链式调用定义模型类

    通过使用可空链式调用可以调用多层属性、方法和下标。这样可以通过各种模型向下访问各种子属性。并且可以根据是否为空判断是否可以访问子属性的属性、方法或下标。

    下面示例代码定义了四个模型类,这些例子包括多层可空链式调用。同时,下面几个知识点也会以这4个类为基础说明:

    //  户主信息
    class Person {
    //  房子信息,可能没有房子,所以可选比较合适
    var residence: Residence?
    }
    //  房子信息
    class Residence {
    //  房间数组
    var rooms = [Room]()
    //  一共有多少房间
    var numberOfRooms: Int {
        return rooms.count
    }
    //  房子地址
    var address: Address?
    //  下标方法,选择第几间房
    subscript(index: Int) -> Room {
        get {
            return rooms[index]     //  取值
        }
        set {
            rooms[index] = newValue    //  设置值
        }
    }
    //  打印房间数量
    func printNumberOfRooms() {
        print("number of rooms : \(rooms.count)")
    }
    }
    //  房间信息
    class Room {
    //  房间名字
    let name: String
    init(roomName name: String) {
        self.name = name
    }
    }
    //  地址信息
    class Address {
    var buildingName: String?  //  建筑名字
    var buildingNumber: String?     //  建筑编号
    var buildingStreet: String?      //  建筑所在街道名称
    //  方法,获取建筑物的唯一标示符,因为可能为空,所以返回的String类型是可选的
    func buildingIdentifier() -> String? {
        if let name = buildingName {
            return name
        } else if let number = buildingNumber {
            return number
        } else {
            return nil
        }
    }
    }
  3. 通过可空链式调用访问属性

    就是我们平常调用属性、方法的那一套,先创建一个实例,然后去调用。其实,很多时候实例、属性等是可空的还是强制展开的,系统都会帮我们去辨别的,我们需要去理解为什么会出现补全的情况,有时候需要我们手动去决定可选还是强制展开。

    下面是代码示例,主要注意点都写在注释里了:

    //  创建一个户主实例
    let john = Person()
    if let roomCount = john.residence?.numberOfRooms {
    //  可空链式调用成功,这里不会执行,因为 john.residence?.numberOfRooms 此时是 nil
    } else {
    //  可空链式调用失败,会执行这里的代码
    }
    //  通过可空链式调用设置属性值
    john.residence = Residence()                //  首先实例化属性  john.residence
    john.residence?.address = Address()         //  实例化地址
    //  此时,address 已经存在,才能修改它的属性值
    john.residence?.address?.buildingName = "北京"
    print(john.residence?.address?.buildingIdentifier())     //  输出: Optional("北京")
    print(john.residence!.address!.buildingIdentifier()!)    //  输出:北京
    /*************** 上面的代码必须每一个属性都是强制展开的,任何一个可空时,都会输出  Optional("北京") ***************/
  4. 通过可空链式调用来调用方法

    可以通过可空链式调用来调用方法,并判断是否调用成功。即使这个方法没有返回值。其实没有返回值的方法隐式的返回Void类型。这意味着没有返回值的方法也会返回 () 或者空的元组。

    如果在可空值上通过可空链式调用来调用没有返回值的方法,那么返回的类型将会是可选空类型 Void?,而不是 Void 。因为空过可空链式调用得到的返回值都是可空 nil 的。

    下面几行示例代码结合上面的定义的数据类型,展示出可空返回值可返回空元组的方法的区别:

    //  不在可空链式调用上调用没有返回值的方法,默认是 Void 类型的返回,返回一个空的元组
    let residence = Residence()
    print(residence.printNumberOfRooms())      //  输出:number of rooms : 0     ()
    //  在可空链式调用上调用没有返回值的方法,返回的是一个可选的空类型 Void? ,因为只有可选类型才能为 nil
    let tom = Person()
    print(tom.residence?.printNumberOfRooms())     //  输出: nil

    同样的,可以判断通过可空链式调用来给属性赋值是否成功。首先,需要知道的是无法给一个 nil 的属性赋值:

    let jack = Person()
    let jackResidence = Residence()
    jack.residence? = jackResidence      //  这句话并没有给 jack.residence? 赋值
    //  此时 jack.residence? == nil
    if (jack.residence? = jackResidence) != nil{
    print("赋值成功")
    } else {
    print("赋值失败")
    }
    //  输出: 赋值失败

    作为对比,对比下面这段代码和上面的区别:

    let jack = Person()
    let jackResidence = Residence()
    jack.residence = jackResidence     //  这句话给 jack.residence 了内存地址
    //  此时 jack.residence 已经被实例了
    if (jack.residence? = jackResidence) != nil{
    print("赋值成功")
    } else {
    print("赋值失败")
    }
    //  输出: 赋值成功
  5. 通过可空链式调用来访问下标

    通过可空链式调用,可以用下标来对空值进行读取或写入,并且判断下标是否调用成功。注意:当通过可空链式调用访问可空值得下标的时候,应该将问号放在下标方括号的前面而不是后面。可空链式调用的问号一般直接跟在可控表达式的后面。问号跟在哪个表达式的后面表示哪个表达式是可控的。

    其实访问下标和访问属性、方法并没有什么不同,可用于 if 语句判断,不能给一个值为 nil 的属性赋值:

    //  访问下标
    let rose = Person()
    if let firstRoomName = rose.residence?.rooms[0].name {
    print("因为  rose.residence? 是可空值,并且此时值为 nil, 所以这句话并不会被打印")    //  不执行
    } else {
    print("被打印的话")        //  执行
    }
    //  给一个值为 nil 的属性赋值
    rose.residence?[0] = Room(roomName: "Living Room")     //  赋值失败,此时 rose.residence?[0] == nil
    rose.residence = Residence()                           //  创建实例
    rose.residence?.rooms.append(Room(roomName: "FirstRoom"))       //  添加值
    //  再次尝试访问下标
    if let firstRoomNameAgain = rose.residence?.rooms[0].name {
    print("因为  rose.residence? 是可空值,并且此时值为 nil, 所以这句话并不会被打印")    //  执行
    } else {
    print("被打印的话")        //  不执行
    }

    如果下标返回可空类型值,比如 Swift 中 Dictionarykey 下标。可以在下标的闭合括号后面放一个问号来链接下标的可空返回值:

    var testScrore = ["john": [88, 91, 78], "jack": [99, 112, 130]]
    testScrore["john"]?[0] = 140
    testScrore["jack"]?[0] = 33
    testScrore["Hehe"]?[0] = 44      //  这句并不会执行,去掉问号会报错
    print(testScrore)      //  输出: ["jack": [33, 112, 130], "john": [140, 91, 78]]
    
  6. 多层链接

    可以通过多个属性的可空链式调用来向下访问属性,方法以及下标。但是多层可空链式调用不会添加返回值的可空性,也就是说:

    • 如果访问的值不是可空的,通过可空链式调用将会返回可空值。
    • 如果访问值得值是已经可空的,通过可空链式调用并不会变得更空。

    因此:

    • 通过可空链式调用访问一个 Int 值,将会返回 Int? ,不论进行了多少次可空链式调用。
    • 类似的,通过可空链式调用访问 Int? 值,并不会变得更空。
  7. 对返回可空的函数进行链接

    上面的例子说明了如何通过可空链式调用来获取可控的属性值。其实还可以通过可空链式调用来调用返回值是可空值的方法,并且可以继续对空值进行链接:

    //  对返回值可空的方法可以继续往下链接
    let liLei = Person()
    //  注意,在方法的圆括号后面加问号并不是指方法可空,而是指方法的返回值可空。
    if let beginWithPrefix = liLei.residence?.address?.buildingIdentifier()?.hasPrefix("China") {
    } else {
    }

结束语

这一小节其实是有点唬人的,它只是把我们平常一直用但是没有太在意的事情说了一遍,所以学起来也是挺轻松的。

我人很懒的,一直学习什么的也是办不到。这两天抽空找了一部动漫看看,绝园的暴风雨,整部动漫围绕莎士比亚的哈姆雷特、暴风雨两个复仇剧展开,显然这么高大上的书我是没读过的,但是动漫本身相当不错,角色都很有特点。喜欢看动漫的可以补一下。

时间: 2024-10-07 11:10:37

Swift-可空链式调用(Optional Chaining)(十五)的相关文章

如何学习Swift可空链式调用

今天我们一起来学习Swift可空链式调用.可空链式调用是一种可以请求和调用属性.方法以及下标的过程,它的可空体现在请求或调用的目标当前可能为nil.如果可空的目标有值,即调用就会成功:如果选择的目标为nil,即调用将返回nil.多个连续的调用可以被链接在一起形成一个调用链,如果其中任何一个节点为nil将导致整个链调用失败.    一.使用可空链式调用来强制展开    可空链的定义,即是在要调用非空的属性.方法.下标的可空值后面添加一个问号即可.特别的,可空链式调用的返回结果与原本的返回结果具有相

Welcome to Swift (苹果官方Swift文档初译与注解三十五)---248~253页(第五章-- 函数 完)

Function Types as Return Types (函数类型作为返回值类型) 一个函数的类型可以作为另一个函数的返回值类型.可以在一个函数的返回值箭头后面写上一个完整的函数类型. 例如: 下面的例子定义了两个简单的函数,分别为stepForward 和 stepBackward.其中stepForward函数返回值比它的输入值多1,stepBackward函数返回值比它输入值少1.这两个函数的 类型都是(Int) -> Int: func stepForward(input: Int

进击的雨燕-------------可空链式调用

详情转自:http://wiki.jikexueyuan.com/project/swift/chapter2/07_Closures.html 可空链式调用(Optional Chaining)是一种可以请求和调用属性.方法及下标的过程,它的可空性体现于请求或调用的目标当前可能为空(nil).如果可空的目标有值,那么调用就会成功:如果选择的目标为空(nil),那么这种调用将返回空(nil).多个连续的调用可以被链接在一起形成一个调用链,如果其中任何一个节点为空(nil)将导致整个链调用失败.

Swift2.1 语法指南——可空链式调用

原档:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html#//apple_ref/doc/uid/TP40014097-CH21-ID245 参考:http://wiki.jikexueyuan.com/project/swift/chapter2/17_Optional_Chaining

《Swift Programming Language 》——可选链(Optional Chaining)

可选链(Optional Chaining)是一种可以请求和调用属性.方法及子脚本的过程,它的自判断性体现于请求或调用的目标当前可能为空(nil).如果自判断的目标有值,那么调用就会成功:相反,如果选择的目标为空(nil),则这种调用将返回空(nil).多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空(nil)将导致整个链失效. 注意: Swift 的自判断链和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且失败与否可以被检测到. 可选链

对照Java学习Swift--可选链式调用(Optional Chaining)

可选链式调用(Optional Chaining)是一种可以在当前值可能为nil的可选值上请求和调用属性.方法及下标的方法.如果可选值有值,那么调用就会成功:如果可选值是nil,那么调用将返回nil.多个调用可以连接在一起形成一个调用链,如果其中任何一个节点为nil,整个调用链都会失败,即返回nil. swift这个功能设计的很好,不会报空指针异常,如果是在Java中,那个环节的对象为空,则报空指针异常,程序异常退出,swift则不会,会直接返回一个nil,很方便. 通过在想调用的属性.方法.或

Optional chaining (可选链)

JavaScript 从成立之初就已经走了很长一段路,提供了许多新的功能,这些功能是专门设计来使该语言更加人性化和提升效率.以下是我最近发现的一些有趣的JavaScript 新增内容.其中一些功能已在 Node,Chrome,Firefox 和 Safari 中可用,而其他功能仍处于建议阶段. Optional chaining (可选链)Optional chaining 可选链使用 ?. 操作符来表示,Optional Chaining 使我们能检查一个对象上面是否存在某属性.其它一些语言有

Swift的可选链,类型转换和扩展

可选链(Optional Chaining) 可选链是一种请求或调用属性,方法,子脚本的过程. 可选性体现于请求或调用的目标当前可能为nil.若不为nil则成功调用,否则返回nil并将链失效. 调用可选链的返回结果与原结果类型相同,但是被包装成为了一个可选类型Optional. 这里由于roomCount返回为nil 所以执行else语句为count赋值为-1. 标注:在Beta3版本发布时对Swift稍微进行了修改,nil变成了关键字,而不再存在NilType的nil了. 可选链可以连续多层调

十六、Optional Chaining

1. 概述 当某个可选值可能为空时,如果要访问它的属性.方法.下标脚本,需要使用Optional chaining 对它进行解包,然后进行访问. 如果这个可选值包含一个值,那么访问成功. 如果这个可选值为 nil ,那么访问返回nil. 多个访问可以组合在一起,成为一个访问链条,任何一个链条为nil,整个链条返回nil. 注意:Swift的 Optional Chaining 和 bjective-C 中的消息为空有些相像,但是Swift可以使用在任意类型中,并且失败与否可以被检测到. 2. 使