《Swift Programming Language 》——可选链(Optional Chaining)

可选链(Optional Chaining)是一种可以请求和调用属性、方法及子脚本的过程,它的自判断性体现于请求或调用的目标当前可能为空(nil)。如果自判断的目标有值,那么调用就会成功;相反,如果选择的目标为空(nil),则这种调用将返回空(nil)。多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空(nil)将导致整个链失效。

注意: Swift 的自判断链和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且失败与否可以被检测到。

可选链可替代强制解析

通过在想调用的属性、方法、或子脚本的可选值(optional value)(非空)后面放一个问号,可以定义一个可选链。这一点很像在可选值后面放一个声明符号来强制拆得其封包内的值。他们的主要的区别在于当可选值为空时可选链即刻失败,然而一般的强制解析将会引发运行时错误。

为了反映可选链可以调用空(nil),不论你调用的属性、方法、子脚本等返回的值是不是可选值,它的返回结果都是一个可选值。你可以利用这个返回值来检测你的可选链是否调用成功,有返回值即成功,返回nil则失败。

调用可选链的返回结果与原本的返回结果具有相同的类型,但是原本的返回结果被包装成了一个可选值,当可选链调用成功时,一个应该返回Int的属性将会返回Int?。

下面几段代码将解释可选链和强制解析的不同。

首先定义两个类Person和Residence。

class Person {
   var residence: Residence?
}

class Residence {
   var numberOfRooms = 1
}

Residence具有一个Int类型的numberOfRooms,其值为 1。Person具有一个自判断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 toretrieve the number of rooms.

这告诉 Swift 来链接自判断residence?属性,如果residence存在则取回numberOfRooms的值。

因为这种尝试获得numberOfRooms的操作有可能失败,可选链会返回Int?类型值,或者称作“自判断Int”。当residence是空的时候(上例),选择Int将会为空,因此会出先无法访问numberOfRooms的情况。

要注意的是,即使numberOfRooms是非自判断Int(Int?)时这一点也成立。只要是通过可选链的请求就意味着最后numberOfRooms总是返回一个Int?而不是Int。

你可以自己定义一个Residence实例给john.residence,这样它就不再为空了:

john.residence = Residence()

john.residence 现在有了实际存在的实例而不是nil了。如果你想使用和前面一样的可选链来获得numberOfRoooms,它将返回一个包含默认值 1 的Int?:

if let roomCount =john.residence?.numberOfRooms {
   println("John's residence has \(roomCount) room(s).")
} else {
   println("Unable to retrieve the number of rooms.")
}
// 打印 "John'sresidence has 1 room(s)"。

为可选链定义模型类

你可以使用可选链来多层调用属性,方法,和子脚本。这让你可以利用它们之间的复杂模型来获取更底层的属性,并检查是否可以成功获取此类底层属性。

后面的代码定义了四个将在后面使用的模型类,其中包括多层可选链。这些类是由上面的Person和Residence模型通过添加一个Room和一个Address类拓展来。

Person类定义与之前相同。

class Person {
   var residence: Residence?
}

Residence类比之前复杂些。这次,它定义了一个变量rooms,它被初始化为一个Room[]类型的空数组:

class Residence {
   var rooms = Room[]()
   var numberOfRooms: Int {
   return rooms.count
    }
   subscript(i: Int) -> Room {
       return rooms[i]
    }
   func printNumberOfRooms() {
       println("The number of rooms is \(numberOfRooms)")
    }
   var address: Address?
}

因为Residence存储了一个Room实例的数组,它的numberOfRooms属性值不是一个固定的存储值,而是通过计算而来的。numberOfRooms属性值是由返回rooms数组的count属性值得到的。

为了能快速访问rooms数组,Residence定义了一个只读的子脚本,通过插入数组的元素角标就可以成功调用。如果该角标存在,子脚本则将该元素返回。

Residence中也提供了一个printNumberOfRooms的方法,即简单的打印房间个数。

最后,Residence定义了一个自判断属性叫address(address?)。Address类的属性将在后面定义。用于rooms数组的Room类是一个很简单的类,它只有一个name属性和一个设定room名的初始化器。

class Room {
   let name: String
   init(name: String) { self.name = name }
}

这个模型中的最终类叫做Address。它有三个自判断属性他们额类型是String?。前面两个自判断属性buildingName和 buildingNumber作为地址的一部分,是定义某个建筑物的两种方式。第三个属性street,用于命名地址的街道名:

class Address {
   var buildingName: String?
   var buildingNumber: String?
   var street: String?
   func buildingIdentifier() -> String? {
       if buildingName {
           return buildingName
       } else if buildingNumber {
           return buildingNumber
       } else {
           return nil
       }
    }
}

Address类还提供了一个buildingIdentifier的方法,它的返回值类型为String?。这个方法检查buildingName和buildingNumber的属性,如果buildingName有值则将其返回,或者如果buildingNumber有值则将其返回,再或如果没有一个属性有值,返回空。

通过可选链调用属性

正如上面“可选链可替代强制解析”中所述,你可以利用可选链的可选值获取属性,并且检查属性是否获取成功。然而,你不能使用可选链为属性赋值。

使用上述定义的类来创建一个人实例,并再次尝试后去它的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 toretrieve the number of rooms。

由于john.residence是空,所以这个可选链和之前一样失败了,但是没有运行时错误。

通过可选链调用方法

你可以使用可选链的来调用可选值的方法并检查方法调用是否成功。即使这个方法没有返回值,你依然可以使用可选链来达成这一目的。

Residence的printNumberOfRooms方法会打印numberOfRooms的当前值。方法如下:

func printNumberOfRooms(){
   println(“The number of rooms is \(numberOfRooms)”)
}

这个方法没有返回值。但是,没有返回值类型的函数和方法有一个隐式的返回值类型Void(参见Function Without Return Values)。

如果你利用可选链调用此方法,这个方法的返回值类型将是Void?,而不是Void,因为当通过可选链调用方法时返回值总是可选类型(optional type)。,即使是这个方法本是没有定义返回值,你也可以使用if语句来检查是否能成功调用printNumberOfRooms方法:如果方法通过可选链调用成功,printNumberOfRooms的隐式返回值将会是Void,如果没有成功,将返回nil:

if john.residence?.printNumberOfRooms() {
   println("It was possible to print the number of rooms.")
} else {
   println("It was not possible to print the number of rooms.")
}
// 打印 "It was notpossible to print the number of rooms."。

使用可选链调用子脚本

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

注意:当你使用可选链来获取子脚本的时候,你应该将问号放在子脚本括号的前面而不是后面。可选链的问号一般直接跟在自判断表达语句的后面。

下面这个例子用在Residence类中定义的子脚本来获取john.residence数组中第一个房间的名字。因为john.residence现在是nil,子脚本的调用失败了。

if let firstRoomName =john.residence?[0].name {
   println("The first room name is \(firstRoomName).")
} else {
   println("Unable to retrieve the first room name.")
}
// 打印 "Unable toretrieve the first room name."。

在子代码调用中可选链的问号直接跟在john.residence的后面,在子脚本括号的前面,因为john.residence是可选链试图获得的可选值。

如果你创建一个Residence实例给john.residence,且在他的rooms数组中有一个或多个Room实例,那么你可以使用可选链通过Residence子脚本来获取在rooms数组中的实例了:

let johnsHouse = Residence()
johnsHouse.rooms += Room(name: "LivingRoom")
johnsHouse.rooms += Room(name:"Kitchen")
john.residence = johnsHouse

if let firstRoomName =john.residence?[0].name {
    println("Thefirst room name is \(firstRoomName).")
} else {
   println("Unable to retrieve the first room name.")
}
// 打印 "The firstroom name is Living Room."。

连接多层链接

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

如果你试图获得的类型不是可选类型,由于使用了可选链它将变成可选类型。如果你试图获得的类型已经是可选类型,由于可选链它也不会提高自判断性。

因此:

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

下面的例子试图获取john的residence属性里的address的street属性。这里使用了两层可选链来联系residence和address属性,他们两者都是可选类型:

if let johnsStreet = john.residence?.address?.street{
   println("John's street name is \(johnsStreet).")
} else {
   println("Unable to retrieve the address.")
}
// 打印 "Unable toretrieve the address.”。

john.residence的值现在包含一个Residence实例,然而john.residence.address现在是nil,因此john.residence?.address?.street调用失败。

从上面的例子发现,你试图获得street属性值。这个属性的类型是String?。因此尽管在可选类型属性前使用了两层可选链,john.residence?.address?.street的返回值类型也是String?。

如果你为Address设定一个实例来作为john.residence.address的值,并为address的street属性设定一个实际值,你可以通过多层可选链来得到这个属性值。

let johnsAddress = Address()
johnsAddress.buildingName = "TheLarches"
johnsAddress.street = "LaurelStreet"
john.residence!.address = johnsAddress

if let johnsStreet =john.residence?.address?.street {
   println("John's street name is \(johnsStreet).")
} else {
   println("Unable to retrieve the address.")
}
// 打印 "John'sstreet name is Laurel Street."。

值得注意的是,“!”符的在定义address实例时的使用(john.residence.address)。john.residence属性是一个可选类型,因此你需要在它获取address属性之前使用!解析以获得它的实际值。

链接自判断返回值的方法

前面的例子解释了如何通过可选链来获得可选类型属性值。你也可以通过调用返回可选类型值的方法并按需链接方法的返回值。

下面的例子通过可选链调用了Address类中的buildingIdentifier 方法。这个方法的返回值类型是String?。如上所述,这个方法在可选链调用后最终的返回值类型依然是String?:

if let buildingIdentifier =john.residence?.address?.buildingIdentifier() {
   println("John's building identifier is\(buildingIdentifier).")
}
// 打印 "John'sbuilding identifier is The Larches."。

如果你还想进一步对方法返回值执行可选链,将可选链问号符放在方法括号的后面:

if let upper =john.residence?.address?.buildingIdentifier()?.uppercaseString {
   println("John's uppercase building identifier is \(upper).")
}
// 打印 "John's uppercasebuilding identifier is THE LARCHES."。

注意:在上面的例子中,你将可选链问号符放在括号后面是因为你想要链接的可选值是buildingIdentifier方法的返回值,不是buildingIdentifier方法本身。

《Swift Programming Language 》——可选链(Optional Chaining)

时间: 2024-10-06 21:17:43

《Swift Programming Language 》——可选链(Optional Chaining)的相关文章

The Swift Programming Language (基础部分)

简介 Swift 是一门开发 iOS, OS X 和 watchOS 应用的新语言.然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 Swift 的很多内容都是你熟悉的. Swift 包含了 C 和 Objective-C 上所有基础数据类型,Int表示整型值:Double和Float表示浮点型值:Bool是布尔型值:String是文本型数据.Swift 还提供了三个基本的集合类型,Array,Set和Dictionary,详见集合类型. 就像 C 语言一样,Swift

[精校版]The Swift Programming Language

通常来说,编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”.在 Swift 中,可以用一行代码实现: println("hello, world") 如果你写过 C 或者 Objective-C 代码,那你应该很熟悉这种形式——在 Swift 中,这行代码就是一个完整的程序.你不需要为了输入输出或者字符串处理导入一个单独的库.全局作用域中的代码会被自动当做程序的入口点,所以你也不需要main函数.你同样不需要在每个语句结尾写上分号. 这个教程会通过一系列编程例子来

IOS开发语言Swift入门连载---可选链

IOS开发语言Swift入门连载-可选链 可选链(Optional Chaining) 是一种可以请求和调用属性.方法及下标脚本的过程,它的可选性体现于请求或调用的目标当前可能为空(nil ).如果可选的目标有值,那么调用就会成功:相反,如果选择的目标为空(nil ),则这种调用将返回空(nil ).多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空(nil )将导致整个链失效. 注意: 的可选链和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中

《The Swift Programming Language 》——闭包 使用方法详细讲解

闭包是自包含的函数代码块,可以在代码中被传递和使用. Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的 lambdas 函数比较相似. 闭包可以捕获和存储其所在上下文中任意常量和变量的引用.这就是所谓的闭合并包裹着这些常量和变量,俗称闭包.Swift 会为您管理在捕获过程中涉及到的所有内存操作. 注意: 如果您不熟悉捕获(capturing)这个概念也不用担心,您可以在值捕获 章节对其进行详细了解. 在函数章节中介绍的全局和嵌套函数实际上

Swift语言入门之旅 (翻译自《The Swift Programming Language》电子书)

关于Swift Swift是为IOS和OSX应用制定的新编程语言,吸取C和Objective-C语言的精粹,但不损失与C语言的兼容性.Swift采用安全编程模型.加入了各种现代编程语言特性,使得该语言更易被掌握.更具扩展性,用起来更有趣.Swift语言的奠基石是已经成熟的.并为大家所喜爱的Cocoa和Cocoa Touch框架,新语言使大家可以尽情畅想新软件开发的机遇. Swift沉积了多年的研发成果,苹果公司为提供高效Swift语言编译器.调试器和基础架构打下了坚实基础.我们使用Automat

The Swift Programming Language 中文版---Swift 初见

Swift 初见 本页内容包括: 简单值(Simple Values) 控制流(Control Flow) 函数和闭包(Functions and Closures) 对象和类(Objects and Classes) 枚举和结构体(Enumerations and Structures) 接口和扩展(Protocols and Extensions) 泛型(Generics) 通常来说,编程语言教程中的第一个程序应该在屏幕上打印"Hello, world".在 Swift 中,可以用

The Swift Programming Language IOS8 快捷编程语言

This is a preliminary document for an API or technology in development. Apple is supplying this information to help you plan for the adoption of the technologies and programming interfaces described herein for use on Apple-branded products. This info

《Swift Programming Language 》——附属脚本(Subscripts)

附属脚本可以定义在类(Class).结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问对象.集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法.举例来说,用附属脚本访问一个数组(Array)实例中的元素可以这样写 someArray[index] ,访问字典(Dictionary)实例中的元素可以这样写 someDictionary[key]. 对于同一个目标可以定义多个附属脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个. 译者:这

《The Swift Programming Language》的笔记-第24页

The Swift Programming Language读书笔记学习笔记 第24页 本页主要内容有两个:打印输出和如何在swift注释代码 1 如何打印变量和常量的值? 使用println函数,细节:println函数除打印变量常量值外还输出一个换行 1) 打印常量 eg1 let hw = "hello chinagame.me" println(hw) => 可将字符串常量输出到Console里 2) 打印变量, 双引号里用 \(变量名) 输出,这个像C语言里的%s eg