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 ...
随机推荐
- nginx学习(二)——基础概念之异步非阻塞
上面讲了很多关于nginx的进程模型,接下来,我们来看看nginx是如何处理事件的. 有人可能要问了,nginx采用多worker的方式来处理请求,每个worker里面只有一个主线程,那能够处理的并发 ...
- OpenSceneGraph FAQ 【转】
1.地球背面的一个点,计算它在屏幕上的坐标,能得到吗? 不是被挡住了吗? 答:计算一个空间点的屏幕坐标,使用osgAPEx::GetScreenPosition函数.当空间点处于相机视空间内(不管它是 ...
- solaris软件管理 FTP
安装一些常用软件 一.应用程序与系统命令的关系: 系统命令文件位置在 /bin /sbin下面或为shell内部指令:完成对系统的基本管理工作:一般在字符操作界面中运行:一般包括命令字.命令选项和命令 ...
- 【Bootstrap】一个兼容IE8、谷歌等主流浏览器的受众门户式风格页面
上一次写的<[Bootstrap]一个兼容IE8.谷歌等主流浏览器的受众巨幕式风格页面>(点击打开链接) 部分老一辈的需求可能对这样的后现代的风格并不惬意, 没关系,我们全然能够改变布局 ...
- swich-----case语句的用法
转: http://xinzhi.wenda.so.com/a/1517927252619839
- 你必须了解的java内存管理机制(二)-内存分配
前言 在上一篇文章中,我们花了较大的篇幅去介绍了JVM的运行时数据区,并且重点介绍了栈区的结构及作用,相关内容请猛戳!在本文中,我们将主要介绍对象的创建过程及在堆中的分配方式. 相关链接(注:文章讲解 ...
- [未完结]数字微分分析法的直线绘制(DDA)
注意! 本文被第1次更新,可能存在后续更新 直线画法 直线的斜截式方程 在二维空间下,一条直线的方程可以被描述为若干种形式,其中比较常见的一种是斜截式方程: \[y=kx+b\] 其中\(k\)称为直 ...
- LeetCode215:Kth Largest Element in an Array
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the so ...
- PHP对称加密类
<?php /** * Created by PhpStorm. * User: zongbinghuang * Date: 2017/7/31 * Time: 15:13 */ namespa ...
- 辛星浅析html5中的role属性
我们使用role属性告诉辅助设备.这个元素所扮演的角色.比方点击的按钮,我们通常就使用role="button",会让这个元素可点击. 可是它很多其它的是用来增强语义性,当现有的h ...