Swift Optional Chaining
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 isnil
, the property, method, or subscript call returnsnil
. Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain isnil
.
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的更多相关文章
- Swift中可选型的Optional Chaining 和 Nil-Coalesce(Swift2.1)
/* 下面是介绍Optional Chaining 和 Nil-Coalesce */ // Optional Chaining (可选链) if let errorMessage = errorMe ...
- Swift Optional
拆包和解包的原因: 其实所谓的 nil 就是 Optional.None, 非 nil 就是Optional.Some, 然后会通过Some(T)包装(wrap)原始值,这也是为什么在使用 Optio ...
- Welcome-to-Swift-17自判断链接(Optional Chaining)
自判断链接(Optional Chaining)是一种可以请求和调用属性.方法及子脚本的过程,它的自判断性体现于请求或调用的目标当前可能为空(nil).如果自判断的目标有值,那么调用就会成功:相反,如 ...
- Optional Chaining as an Alternative to Forced Unwrapping
?与!的区别 You specify optional chaining by placing a question mark (?) after the optional value on whic ...
- 精读《Optional chaining》
1. 引言 备受开发者喜爱的特性 Optional chaining 在 2019.6.5 进入了 stage2,让我们详细读一下草案,了解一下这个特性的用法以及讨论要点. 借着这次精读草案,让我们了 ...
- Swift -> Optional嵌套 探讨
准备运动:Optional 的介绍 王巍的<Swifter>一书中,介绍了一个有用的命令:在 LLDB 中输入 fr v -R foo,可以查看foo 这个变量的内存构成.我们稍后的分析将 ...
- [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 ...
- js optional chaining operator
js optional chaining operator js 可选链 可选链操作符( ?. )允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效. ?. 操作符的功能类似于 ...
- TypeScript 3.7 RC & Optional Chaining
TypeScript 3.7 RC & Optional Chaining https://devblogs.microsoft.com/typescript/announcing-types ...
随机推荐
- xshell登陆腾讯云服务器
2016-12-11 00:17:36 前段时间在同学的介绍下关注了一下腾讯云:然后里面有学生优惠可以拿到免费的域名和云服务器.所以感兴趣就实验了一下,今天中午抢到了“1元特惠的学生包”,里面有免 ...
- C++简单介绍
一.怎样用C++的源文件产生一个可运行程序 一个C++程序由一个或者多个编译单元组成.每一个编译单元都是一个独立的源码文件.一般是一个带.cpp的文件,编译器每次编一个文件编译单元,生成一个以.obj ...
- quick-cocos2d-x教程12:实现文本和password输入界面
用户输入账号和password输入是常见工能,如今用editbox来实现username和password输入.可是这个商业项目上常见功能,网上却没有找到教程.我就一步一步的实现.代码例如以下: fu ...
- vim调试
首先,想调试一个程序的话,输入以下命令: guest-djjtew@ubuntu:~$ python3 -m pdb 1.py 这时候就停止了,等待着你的输入,然后输入"l"的话, ...
- ubuntu 14.04 LTS 安装webbentch压力測试工具
近期在做 压力測试工具,除了apache的ab測试工具外,发现webbentch工具也不错,这里简介下这两个工具. 一.webbentch安装: wget http://blog.s135.com/s ...
- 惊艳的cygwin——Windows下的Linux命令行环境的配置和使用
http://www.tuicool.com/articles/2MramqI 时间 2014-07-29 09:28:36 点滴之间 聚沙成金 原文 http://www.path8.net/t ...
- 命令行查看memcached的运行状态(转载)
很多时候需要监控服务器上的Memcached运行情况,比如缓存的查询次数,命中率之类的.但找到的那个memcached-tool是linux下用perl写的,我也没试过windows能不能用.后来发现 ...
- poj 1163 The Triangle &poj 3176 Cow Bowling (dp)
id=1163">链接:poj 1163 题意:输入一个n层的三角形.第i层有i个数,求从第1层到第n层的全部路线中.权值之和最大的路线. 规定:第i层的某个数仅仅能连线走到第i+1层 ...
- android日历控件
源码地址 : http://download.csdn.net/detail/abc13939746593/7265459
- 【BZOJ2729】[HNOI2012]排队 组合数
[BZOJ2729][HNOI2012]排队 Description 某中学有 n 名男同学,m 名女同学和两名老师要排队参加体检.他们排成一条直线,并且任意两名女同学不能相邻,两名老师也不能相邻,那 ...