Swift之深入解析可选链的功能和使用
一、什么是可选链?
- 可选链(Optional Chaining)是一种可以请求和调用属性、方法和子脚本的过程,用于请求或调用的目标可能为nil。
- 可选链返回两个值:
- 如果目标有值,调用就会成功,返回该值;
- 如果目标为 nil,调用将返回 nil。
- 多次请求或调用可以被链接成一个链,如果任意一个节点为 nil 将导致整条链失效。
二、可选链可替代强制解析
- 通过在属性、方法、或下标脚本的可选值后面放一个问号(?),即可定义一个可选链。
可选链 ‘?’ | 感叹号(!)强制展开方法,属性,下标脚本可选链 |
---|---|
? 放置于可选值后来调用方法,属性,下标脚本 | ! 放置于可选值后来调用方法,属性,下标脚本来强制展开值 |
当可选为 nil 输出比较友好的错误信息 | 当可选为 nil 时强制展开执行错误 |
① 使用感叹号(!)可选链
- 如下所示:
class Person {var residence: Residence?
}class Residence {var numberOfRooms = 1
}let john = Person()// 将导致运行时错误
let roomCount = john.residence!.numberOfRooms
- 执行结果为:
fatal error: unexpectedly found nil while unwrapping an Optional value
- 想使用感叹号(!)强制解析获得这个人 residence 属性 numberOfRooms 属性值,将会引发运行时错误,因为这时没有可以供解析的 residence 值。
② 使用问号(?)可选链
- 如下所示:
class Person {var residence: Residence?
}class Residence {var numberOfRooms = 1
}let john = Person()// 链接可选residence?属性,如果residence存在则取回numberOfRooms的值
if let roomCount = john.residence?.numberOfRooms {print("John 的房间号为 \(roomCount)。")
} else {print("不能查看房间号")
}
- 执行结果为:
不能查看房间号
- 因为这种尝试获得 numberOfRooms 的操作有可能失败,可选链会返回 Int? 类型值,或者称作“可选Int”。当 residence 是空的时候,选择 Int 将会为空,因此会出现无法访问 numberOfRooms 的情况。
- 要注意的是,即使 numberOfRooms 是非可选 Int(Int?)时这一点也成立。只要是通过可选链的请求就意味着最后 numberOfRooms 总是返回一个 Int? 而不是 Int。
三、为可选链定义模型类
- 可以使用可选链来多层调用属性、方法和下标脚本,这可以利用它们之间的复杂模型来获取更底层的属性,并检查是否可以成功获取此类底层属性。
- 如下所示,定义了四个模型类,其中包括多层可选链:
class Person {var residence: Residence?
}// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {var rooms = [Room]()var numberOfRooms: Int {return rooms.count}subscript(i: Int) -> Room {return rooms[i]}func printNumberOfRooms() {print("房间号为 \(numberOfRooms)")}var address: Address?
}// Room 定义一个name属性和一个设定room名的初始化器
class Room {let name: Stringinit(name: String) { self.name = name }
}// 模型中的最终类叫做Address
class Address {var buildingName: String?var buildingNumber: String?var street: String?func buildingIdentifier() -> String? {if (buildingName != nil) {return buildingName} else if (buildingNumber != nil) {return buildingNumber} else {return nil}}
}
四、通过可选链调用方法
- 可以使用可选链的来调用可选值的方法并检查方法调用是否成功,即使这个方法没有返回值,依然可以使用可选链来达成这一目的。
- 如下所示:
class Person {var residence: Residence?
}// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {var rooms = [Room]()var numberOfRooms: Int {return rooms.count}subscript(i: Int) -> Room {return rooms[i]}func printNumberOfRooms() {print("房间号为 \(numberOfRooms)")}var address: Address?
}// Room 定义一个name属性和一个设定room名的初始化器
class Room {let name: Stringinit(name: String) { self.name = name }
}// 模型中的最终类叫做Address
class Address {var buildingName: String?var buildingNumber: String?var street: String?func buildingIdentifier() -> String? {if (buildingName != nil) {return buildingName} else if (buildingNumber != nil) {return buildingNumber} else {return nil}}
}let john = Person()if ((john.residence?.printNumberOfRooms()) != nil) {print("输出房间号")
} else {print("无法输出房间号")
}
- 执行输出结果为:
无法输出房间号
- 使用 if 语句来检查是否能成功调用 printNumberOfRooms 方法:如果方法通过可选链调用成功,printNumberOfRooms 的隐式返回值将会是 Void,如果没有成功,将返回 nil。
五、使用可选链调用下标脚本
- 可以使用可选链来尝试从下标脚本获取值并检查下标脚本的调用是否成功,然而,不能通过可选链来设置下标脚本。
- 如下所示:
class Person {var residence: Residence?
}// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {var rooms = [Room]()var numberOfRooms: Int {return rooms.count}subscript(i: Int) -> Room {return rooms[i]}func printNumberOfRooms() {print("房间号为 \(numberOfRooms)")}var address: Address?
}// Room 定义一个name属性和一个设定room名的初始化器
class Room {let name: Stringinit(name: String) { self.name = name }
}// 模型中的最终类叫做Address
class Address {var buildingName: String?var buildingNumber: String?var street: String?func buildingIdentifier() -> String? {if (buildingName != nil) {return buildingName} else if (buildingNumber != nil) {return buildingNumber} else {return nil}}
}let john = Person()
if let firstRoomName = john.residence?[0].name {print("第一个房间名 \(firstRoomName).")
} else {print("无法检索到房间")
}
- 执行输出结果为:
无法检索到房间
- 在下标脚本调用中可选链的问号直接跟在 john.residence 的后面,在下标脚本括号的前面,因为 john.residence 是可选链试图获得的可选值。
- 如下所示,创建一个 Residence 实例给 john.residence,且在他的 rooms 数组中有一个或多个 Room 实例,那么可以使用可选链通过 Residence 下标脚本来获取在 rooms 数组中的实例:
class Person {var residence: Residence?
}// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {var rooms = [Room]()var numberOfRooms: Int {return rooms.count}subscript(i: Int) -> Room {return rooms[i]}func printNumberOfRooms() {print("房间号为 \(numberOfRooms)")}var address: Address?
}// Room 定义一个name属性和一个设定room名的初始化器
class Room {let name: Stringinit(name: String) { self.name = name }
}// 模型中的最终类叫做Address
class Address {var buildingName: String?var buildingNumber: String?var street: String?func buildingIdentifier() -> String? {if (buildingName != nil) {return buildingName} else if (buildingNumber != nil) {return buildingNumber} else {return nil}}
}let john = Person()
let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "客厅"))
johnsHouse.rooms.append(Room(name: "厨房"))
john.residence = johnsHouselet johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence!.address = johnsAddressif let johnsStreet = john.residence?.address?.street {print("John 所在的街道是 \(johnsStreet)。")
} else {print("无法检索到地址。 ")
}
- 执行输出结果为:
John 所在的街道是 Laurel Street。
六、通过可选链接调用来访问下标
- 通过可选链接调用,我们可以用下标来对可选值进行读取或写入,并且判断下标调用是否成功。
- 如下所示:
class Person {var residence: Residence?
}// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {var rooms = [Room]()var numberOfRooms: Int {return rooms.count}subscript(i: Int) -> Room {return rooms[i]}func printNumberOfRooms() {print("房间号为 \(numberOfRooms)")}var address: Address?
}// Room 定义一个name属性和一个设定room名的初始化器
class Room {let name: Stringinit(name: String) { self.name = name }
}// 模型中的最终类叫做Address
class Address {var buildingName: String?var buildingNumber: String?var street: String?func buildingIdentifier() -> String? {if (buildingName != nil) {return buildingName} else if (buildingNumber != nil) {return buildingNumber} else {return nil}}
}let john = Person()let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "客厅"))
johnsHouse.rooms.append(Room(name: "厨房"))
john.residence = johnsHouseif let firstRoomName = john.residence?[0].name {print("第一个房间名为\(firstRoomName)")
} else {print("无法检索到房间")
}
- 执行输出结果为:
第一个房间名为客厅
七、访问可选类型的下标
- 如果下标返回可空类型值,比如 Swift 中 Dictionary 的 key 下标,可以在下标的闭合括号后面放一个问号来链接下标的可空返回值。
- 如下所示,定义了一个 testScores 数组,包含了两个键值对, 把 String 类型的 key 映射到一个整形数组:
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0]++
testScores["Brian"]?[0] = 72
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]
- 用可选链接调用把"Dave"数组中第一个元素设为 91,把"Bev"数组的第一个元素 +1,然后尝试把"Brian"数组中的第一个元素设为 72。
- 前两个调用是成功的,因为这两个 key 存在,但是 key"Brian"在字典中不存在,所以第三个调用失败。
八、连接多层链接
- 可以将多层可选链连接在一起,可以掘取模型内更下层的属性方法和下标脚本,然而多层可选链不能再添加比已经返回的可选值更多的层。
- 如果试图通过可选链获得 Int 值,不论使用了多少层链接返回的总是 Int?。相似的,如果试图通过可选链获得 Int?值,不论使用了多少层链接返回的总是 Int?。
- 如下所示,试图获取 john 的 residence 属性里的 address 的 street 属性,使用了两层可选链来联系 residence 和 address 属性,它们两者都是可选类型:
class Person {var residence: Residence?
}// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {var rooms = [Room]()var numberOfRooms: Int {return rooms.count}subscript(i: Int) -> Room {return rooms[i]}func printNumberOfRooms() {print("房间号为 \(numberOfRooms)")}var address: Address?
}// Room 定义一个name属性和一个设定room名的初始化器
class Room {let name: Stringinit(name: String) { self.name = name }
}// 模型中的最终类叫做Address
class Address {var buildingName: String?var buildingNumber: String?var street: String?func buildingIdentifier() -> String? {if (buildingName != nil) {return buildingName} else if (buildingNumber != nil) {return buildingNumber} else {return nil}}
}let john = Person()if let johnsStreet = john.residence?.address?.street {print("John 的地址为 \(johnsStreet).")
} else {print("不能检索地址")
}
- 执行结果为:
不能检索地址
- 如果为 Address 设定一个实例来作为 john.residence.address 的值,并为 address 的 street 属性设定一个实际值,可以通过多层可选链来得到这个属性值。
class Person {var residence: Residence?
}class Residence {var rooms = [Room]()var numberOfRooms: Int {return rooms.count}subscript(i: Int) -> Room {get{return rooms[i]}set {rooms[i] = newValue}}func printNumberOfRooms() {print("房间号为 \(numberOfRooms)")}var address: Address?
}class Room {let name: Stringinit(name: String) { self.name = name }
}class Address {var buildingName: String?var buildingNumber: String?var street: String?func buildingIdentifier() -> String? {if (buildingName != nil) {return buildingName} else if (buildingNumber != nil) {return buildingNumber} else {return nil}}
}
let john = Person()
john.residence?[0] = Room(name: "浴室")let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "客厅"))
johnsHouse.rooms.append(Room(name: "厨房"))
john.residence = johnsHouseif let firstRoomName = john.residence?[0].name {print("第一个房间是\(firstRoomName)")
} else {print("无法检索房间")
}
- 执行输出结果为:
第一个房间是客厅
九、对返回可选值的函数进行链接
- 还可以通过可选链接来调用返回可空值的方法,并且可以继续对可选值进行链接。
- 如下所示:
class Person {var residence: Residence?
}// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {var rooms = [Room]()var numberOfRooms: Int {return rooms.count}subscript(i: Int) -> Room {return rooms[i]}func printNumberOfRooms() {print("房间号为 \(numberOfRooms)")}var address: Address?
}// Room 定义一个name属性和一个设定room名的初始化器
class Room {let name: Stringinit(name: String) { self.name = name }
}// 模型中的最终类叫做Address
class Address {var buildingName: String?var buildingNumber: String?var street: String?func buildingIdentifier() -> String? {if (buildingName != nil) {return buildingName} else if (buildingNumber != nil) {return buildingNumber} else {return nil}}
}let john = Person()if john.residence?.printNumberOfRooms() != nil {print("指定了房间号)")
} else {print("未指定房间号")
}
- 执行结果为:
未指定房间号
Swift之深入解析可选链的功能和使用相关推荐
- Swift之深入解析Key Paths的功能与应用
一.前言 自从 Swift 刚开始就被设计为是编译时安全和静态类型后,它就缺少了那种经常在运行时语言中的动态特性,比如 Object-C, Ruby 和 JavaScript.举个例子,在 Objec ...
- Swift之深入解析可选类型Optional的底层原理
一.Optional 简介 Swift 的可选(Optional)类型,用于处理值缺失的情况,可选表示"那儿有一个值,并且它等于 x "或者"那儿没有值". S ...
- swift 注意事项 (十六) —— 可选链
可选链(Optional Chaining) 我们都知道"可选型"是什么.那么可选链又是什么,举个样例解释一下: struct MyName{ var name } struct ...
- Swift中文教程(十七) 可选链
可选链(Optional Chaining)是一种可以请求和调用属性.方法及子脚本的过程,它的自判断性体现于请求或调用的目标当前可能为空(nil).如果自判断的目标有值,那么调用就会成功:相反,如果选 ...
- swift 可选链_Swift可选链
swift 可选链 Swift Optional Chaining is a very useful feature. Optionals are a type in Swift that wrap ...
- 《从零开始学Swift》学习笔记(Day 26)——可选链
Swift2.0学习笔记(Day 26)--可选链 原创文章,欢迎转载.转载请注明:关东升的博客 在Swift程序表达式中会看到问号(?)和感叹号(!),它们代表什么含义呢?这些符号都与可选类型 ...
- Swift之深入解析如何避免单元测试中的强制解析
一.前言 强制解析(使用 !)是 Swift 语言中不可或缺的一个重要特点(特别是和 Objective-C 的接口混合使用时),它回避了一些其他问题,使得 Swift 语言变得更加优秀. 比如在我的 ...
- JS 特性:可选链(?.)
可选链操作符 参考文献: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Optional_ch ...
- Swift之深入解析如何使用Xcode和LLDB v2修改UI元素
一.前言 在上一篇博客中,已经详细地介绍如何使用 LLDB 表达式修改 UI 元素,具体请参考:Swift之深入解析如何将代码添加为自定义LLDB命令. 在这篇博客中,将继续讨论相同的问题需求,并将重 ...
最新文章
- HomeKit推出已两年 但是苹果在智能家居上却被对手甩开了差距
- 使用postman测试oauth2.0认证服务中出现OAuth出现Bad credentials
- centos php配置mysql数据库_CentOS+Nginx+PHP+MySQL详细配置(图解)
- lisp语言画阿基米德线_中国油画艺术表达着艺术家思想情感,而且展现了油画语言独特之美...
- 抓取xen-tools生成的信息
- 爬取微博的数据时别人用的是FM.view方法传递html标签那么jsoup怎么解析呢
- dch中mysql作用_解析Cloudera Manager内部结构、功能包括配置文件、目录位置等
- HanLP: Han Language Processing
- JavaScript零基础入门 11:JavaScript实现图片上传并预览
- win的反义词_英语近反义词辨析
- MongoDB快速入门学习
- 关于Word的累加符号上下标变右标的解决办法
- 任务列表,任务办理,转办任务,委派任务
- 优维EasyOps,打造新一代运维新方式
- Linux下system () 函数详解简介
- 是时候完全转向无服务器化了吗?
- FancyCache Volume 0.8.0
- C#使用MongoDB
- Browser-sync安装与使用
- 生成二维码 并可以点击下载(多个二维码一起渲染)
热门文章
- IT行业分析之企业信息化技术
- phoenix kerberos 连接配置
- linux下部署tomcat服务器之安装jdk
- EventSource
- “未能加载文件或程序集“EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”...
- 调用cmd不显示黑框的方法
- Firefox, IE等不同浏览器对JavaScript,CSS不同解析问题 (转备以后使用)
- 【数据结构总结】第六章 图(非线性结构)
- c语言不安全库_为什么和其他语言相比C语言是快速的语言?
- java redis 没密码配置_如何解决redis的NOAUTH Authentication required异常