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: String

init(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: String

init(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。

使用可选链调用下标脚本

你可以使用可选链来尝试从下标脚本获取值并检查下标脚本的调用是否成功,然而,你不能通过可选链来设置下标脚本。

实例1

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: String

init(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("无法检索到房间")

}

以上程序执行输出结果为:

无法检索到房间

在下标脚本调用中可选链的问号直接跟在 circname.print 的后面,在下标脚本括号的前面,因为circname.print是可选链试图获得的可选值。

实例2

实例中创建一个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: String

init(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 = johnsHouse

let johnsAddress = Address()

johnsAddress.buildingName = "The Larches"

johnsAddress.street = "Laurel Street"

john.residence!.address = johnsAddress

if 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: String

init(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 = johnsHouse

if let firstRoomName = john.residence?[0].name {

print("第一个房间名为\(firstRoomName)")

} else {

print("无法检索到房间")

}

以上程序执行输出结果为:

第一个房间名为客厅

访问可选类型的下标

如果下标返回可空类型值,比如Swift中Dictionary的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]

上面的例子中定义了一个testScores数组,包含了两个键值对, 把String类型的key映射到一个整形数组。

这个例子用可选链接调用把"Dave"数组中第一个元素设为91,把"Bev"数组的第一个元素+1,然后尝试把"Brian"数组中的第一个元素设为72。

前两个调用是成功的,因为这两个key存在。但是key"Brian"在字典中不存在,所以第三个调用失败。

连接多层链接

你可以将多层可选链连接在一起,可以掘取模型内更下层的属性方法和下标脚本。然而多层可选链不能再添加比已经返回的可选值更多的层。

如果你试图通过可选链获得Int值,不论使用了多少层链接返回的总是Int?。 相似的,如果你试图通过可选链获得Int?值,不论使用了多少层链接返回的总是Int?。

实例1

下面的例子试图获取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: String

init(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("不能检索地址")

}

以上程序执行输出结果为:

不能检索地址

实例2

如果你为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: String

init(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 = johnsHouse

if 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: String

init(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("未指定房间号")

}

以上程序执行输出结果为:

未指定房间号

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,719评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,337评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,887评论 0 324
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,488评论 1 266
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,313评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,284评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,672评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,346评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,644评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,700评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,457评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,316评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,706评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,261评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,648评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,859评论 2 335

推荐阅读更多精彩内容