Optional Chaining介绍

关于「optional chaining」,《The Swift Programming Language》是这么描述的:

Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil. If the optional contains a value, the property, method, or subscript call succeeds; if the optional is nil, the property, method, or subscript call returns nil. Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil.

P.S:「Optional Chaining」有各种各样的翻译,譬如「自判断链接」「可选链表」等,个人觉得「可选链表」还凑合,本文尽量使用英文表述。

P.S:Swift中的「optional chaining」有些类似于OC中「向nil发送消息」,简而言之,在OC中,向一个对象(指针)发送消息时,若这个指针为nil,则啥都不干;Swift的「optional chaining」有类似的效果;只不过OC这种处理只适用于类对象,Swift的「optional chaining」适用于任意类型。此外,在OC中,我们想一个可能为nil的对象发送一个消息时,通常我们不知道这个消息是否被执行了(尤其是该消息么有返回值时),但在Swift中,可以通过检查返回值来判断实例的方法是否被调用,后文会详细阐述。

可选链表 v.s 强制解包

在Swift中,访问某些属性/方法/下标得到的经果常常是一个optional,在这些返回值为optional的属性/方法/下标后放上一个问号?就构成了所谓的「optional chaining」。
这有一些类似于强制解包(在可选类型后面放上一个感叹号!强制解包)。他们的在于当optional为nil时「optional chaining」即刻失败,然而一般的强制解包操作会引发runtime错误。

下面这段话非常重要。
因为「optional chaining」是随时否可以提前返回nil的,所以使用optional chaining所得到的东西都是optional,even if the property, method, or subscript you are querying returns a non-optional value. 下面以代码来演示:

class Toy {
let name: String
init(name: String) {
self.name = name
}
} class Pet {
var toy: Toy?
} class Child {
var pet: Pet?
}

在实际使用过程中,我们想要知道小明(名为xiaoming的Child对象)的宠物的玩具的名字的时候,可以通过下面的「optional chaining」查询:

let toyName = xiaoming.pet?.toy?.name

注意,我们最后访问的是name,并且在Toy的定义中name是被定义为一个确定的String而非String?的,但是我们拿到的toyName起始还是一个String?的类型。这是由于在「optional chaining」中我们在任意一个?.的时候都可能遇到nil而提前返回,这个时候当然只能获取到nil了。

所以,在实际使用中,我们大多数情况下可能更希望使用「Optional Binding」来直接取值,如下:

if let toyName = xiaoming.pet?.toy?.name {
// 巴拉巴拉
}

总之,使用「optional chaining」的返回结果一定是一个optional。

OK,现在以几段代码来解释「可选链表」(即?.)和「强制解包」(即!.)的不同。

首先定义两个类Person和Residence,如下:

class Person {
var residence: Residence?
} class Residence {
var numberOfRooms =
}

Residence具有一个Int类型属性numberOfRooms,其值为1。Person具有一个optional属性residence,它的类型是Residence?

如果你创建一个新的Person实例,它的residence属性由于是被定义为自判断型的,此属性将默认初始化为空:

let john = Person()

如果你想使用声明符!强制解包获得这个人residence属性的numberOfRooms属性值,将会引发运行时错误,因为这时没有可以供拆包的residence值,如下:

let roomCount = john.residence!.numberOfRooms
// 将导致运行时错误

当john.residence不是nil时,会正常运行,且会将roomCount设置为一个Int类型的合理值。然而,如上所述,当residence为空时,这个代码将会导致运行时错误。

自判断链接提供了一种另一种获得numberOfRooms的方法。利用自判断链接,使用问号来代替原来!的位置:

if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
} /* 输出:
Unable to retrieve the number of rooms.
*/

通过Optional Chaining访问属性

正如上文可选链表 v.s 强制解包所述,你可以利用「optional chaining」获取属性,并且检查属性是否成功。

使用上述定义的类来创建一个人实例,并再次尝试后去它的numberOfRooms属性:

let john = Person()
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
/*输出:
Unable to retrieve the number of rooms.
*/

由于john.residence是空,所以这个自判断链接和之前一样失败了,但是没有运行时错误。

你还可以使用「optional chaining」来设置属性值,如下:

john.residence?.numberOfRooms = 

通过Optional Chaining调用方法

你可以使用「optional chaining」的调用某个optional的方法,并可以检查方法调用是否成功,哪怕这个方法没有返回值。

在上文的Residence中添加一个实例方法printNumberOfRooms,该方法会打印numberOfRooms的当前值。方法如下:

func printNumberOfRooms(){
println("The number of rooms is \(numberOfRooms)")
}

这个方法没有返回值。但是,在Swift中,若函数和方法没有显式提供返回值,则Swift会为它们提供一个隐式的返回值类型Void。如果你利用自判断链接调用此方法,这个方法的返回值类型将是Void?,而不是Void,因为当通过「optional chaining」调用方法时返回值总是optional,即使是这个方法本是没有定义返回值,你也可以使用if语句来检查是否能成功调用printNumberOfRooms方法:如果方法通过自判断链接调用成功,printNumberOfRooms的隐式返回值将会是Void,如果没有成功,将返回nil:

if john.residence?.printNumberOfRooms() != nil {
println("It was possible to print the number of rooms.")
} else {
println("It was not possible to print the number of rooms.")
}
/*输出:
It was not possible to print the number of rooms.
*/

通过Optional Chaining访问下标

还可以在「optional chaining」中通过「下标」(subscripts)获取和设置一个optional,同样可以检查是否获取成功;

通过「Optional Chaining」访问下标时,一定要注意:

When you access a subscript on an optional value through optional chaining, you place the question mark (?before the subscript’s braces ([]), not after. The optional chaining question mark always follows immediately after the part of the expression that is optional.

举个栗子,在上文Person和Residence的基础上添加一个新的类Room:

class Room {
let name: String
init(name: String) {
self.name = name
}
}

在Residence类中添加一个subscript和一个数组属性rooms,如下:

class Residence {
...
var rooms = [Room]()
subscript(i: Int) -> Room {
return rooms[i]
}
...
}

通过「Optional Chaining」访问下标如下:

if let firstRoomName = john.residence?[].name {
println("The first room name is \(firstRoomName).")
} else {
println("Unable to retrieve the first room name.")
} /*输出:
Unable to retrieve the first room name.
*/

同样,除了获取,还可以设置:

john.residence?[] = Room(name: "Bashroom")

Swift Optional Chaining的更多相关文章

  1. Swift中可选型的Optional Chaining 和 Nil-Coalesce(Swift2.1)

    /* 下面是介绍Optional Chaining 和 Nil-Coalesce */ // Optional Chaining (可选链) if let errorMessage = errorMe ...

  2. Swift Optional

    拆包和解包的原因: 其实所谓的 nil 就是 Optional.None, 非 nil 就是Optional.Some, 然后会通过Some(T)包装(wrap)原始值,这也是为什么在使用 Optio ...

  3. Welcome-to-Swift-17自判断链接(Optional Chaining)

    自判断链接(Optional Chaining)是一种可以请求和调用属性.方法及子脚本的过程,它的自判断性体现于请求或调用的目标当前可能为空(nil).如果自判断的目标有值,那么调用就会成功:相反,如 ...

  4. Optional Chaining as an Alternative to Forced Unwrapping

    ?与!的区别 You specify optional chaining by placing a question mark (?) after the optional value on whic ...

  5. 精读《Optional chaining》

    1. 引言 备受开发者喜爱的特性 Optional chaining 在 2019.6.5 进入了 stage2,让我们详细读一下草案,了解一下这个特性的用法以及讨论要点. 借着这次精读草案,让我们了 ...

  6. Swift -> Optional嵌套 探讨

    准备运动:Optional 的介绍 王巍的<Swifter>一书中,介绍了一个有用的命令:在 LLDB 中输入 fr v -R foo,可以查看foo 这个变量的内存构成.我们稍后的分析将 ...

  7. [TypeScript] Optional Chaining with TypeScript 3.7

    TypeScript 3.7 adds support for optional chaining. This lesson shows you how to use it in your code ...

  8. js optional chaining operator

    js optional chaining operator js 可选链 可选链操作符( ?. )允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效. ?. 操作符的功能类似于 ...

  9. TypeScript 3.7 RC & Optional Chaining

    TypeScript 3.7 RC & Optional Chaining https://devblogs.microsoft.com/typescript/announcing-types ...

随机推荐

  1. xshell登陆腾讯云服务器

    2016-12-11   00:17:36 前段时间在同学的介绍下关注了一下腾讯云:然后里面有学生优惠可以拿到免费的域名和云服务器.所以感兴趣就实验了一下,今天中午抢到了“1元特惠的学生包”,里面有免 ...

  2. C++简单介绍

    一.怎样用C++的源文件产生一个可运行程序 一个C++程序由一个或者多个编译单元组成.每一个编译单元都是一个独立的源码文件.一般是一个带.cpp的文件,编译器每次编一个文件编译单元,生成一个以.obj ...

  3. quick-cocos2d-x教程12:实现文本和password输入界面

    用户输入账号和password输入是常见工能,如今用editbox来实现username和password输入.可是这个商业项目上常见功能,网上却没有找到教程.我就一步一步的实现.代码例如以下: fu ...

  4. vim调试

    首先,想调试一个程序的话,输入以下命令: guest-djjtew@ubuntu:~$ python3 -m pdb 1.py 这时候就停止了,等待着你的输入,然后输入"l"的话, ...

  5. ubuntu 14.04 LTS 安装webbentch压力測试工具

    近期在做 压力測试工具,除了apache的ab測试工具外,发现webbentch工具也不错,这里简介下这两个工具. 一.webbentch安装: wget http://blog.s135.com/s ...

  6. 惊艳的cygwin——Windows下的Linux命令行环境的配置和使用

    http://www.tuicool.com/articles/2MramqI 时间 2014-07-29 09:28:36  点滴之间 聚沙成金 原文  http://www.path8.net/t ...

  7. 命令行查看memcached的运行状态(转载)

    很多时候需要监控服务器上的Memcached运行情况,比如缓存的查询次数,命中率之类的.但找到的那个memcached-tool是linux下用perl写的,我也没试过windows能不能用.后来发现 ...

  8. poj 1163 The Triangle &amp;poj 3176 Cow Bowling (dp)

    id=1163">链接:poj 1163 题意:输入一个n层的三角形.第i层有i个数,求从第1层到第n层的全部路线中.权值之和最大的路线. 规定:第i层的某个数仅仅能连线走到第i+1层 ...

  9. android日历控件

    源码地址 : http://download.csdn.net/detail/abc13939746593/7265459

  10. 【BZOJ2729】[HNOI2012]排队 组合数

    [BZOJ2729][HNOI2012]排队 Description 某中学有 n 名男同学,m 名女同学和两名老师要排队参加体检.他们排成一条直线,并且任意两名女同学不能相邻,两名老师也不能相邻,那 ...