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 ...
随机推荐
- scala工具sbt的安装和使用;idea如何创建scala项目
scala的sbt类似于java的maven mac:brew install sbt linux:yum Install sbt 或者下载二机制包 使用sbt需要想mvn一样搭建公司私服,不然,下载 ...
- Linux包括hash_map和hash_set的not declared问题
当在Linux下cpp文件包括hash_map或hash_set时.会出现"'hash_map' was not declared in this scope"问题. #inclu ...
- redis 在我做的容器中的配置路劲
配置 /etc/redis/redis.conf 数据库位置 /var/lib/redis/dump.rdb
- 实现TextView中link的点击效果
朋友们,你们在TextView处理link的时候是不是一直被苦逼的android默认的方式困扰?每次点击link的时候,点击效果是整个textview来响应.非常烂吧?原因就不多赘述了. 那么以下这个 ...
- 如何打造你的独特观点(一) ——形成“自己的想法”的基础课zz
信息过载的时代,能在各种KOL的声音中保持独立思考很不容易,能输出独特观点又进一层.不断练习我们独立思考的能力,有助于看清周围复杂的事物,也能让我们在日常生活中给人留下“有趣之人”的印象,提升人际交往 ...
- iOS之简单瀑布流的实现
iOS之简单瀑布流的实现 前言 超简单的瀑布流实现,这里说一下笔者的思路,详细代码在这里. 实现思路 collectionView能实现各中吊炸天的布局,其精髓就在于UICollectionVie ...
- mysql 中创建递归函数
1.在navicat中创建递归函数 BEGIN ) DEFAULT ''; ) default ''; ) default ''; ) default rootId; ; WHILE rootId i ...
- leetCode(51):Valid Palindrome
Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignori ...
- 学习某些API的方法
学习某些 API 的方法 这里的 API 可能是某个系统平台,开发包,开发平台,开发工具等等,因为任何系统和技术方法提供给开发者的打包方式都是一系列 API . 无论你有在哪一层级开发,从硬件驱动到系 ...
- 计算机鼻祖-Donald Knuth(高纳德) 的传奇
李开复说,练内功,不要仅仅花功夫学习各种流行的编程语言和工具,以及一些公司招聘广告上要求的科目.要把数据结构.算法.数据库.操作系统原理.计算机体系结构.计算机网络,离散数学等基础课程学好.最好还是试 ...