swift可选链_Optional Chaining

分享到:

可选链接是查询和调用属性、类函数以及目前可能为nil的可选项上的下标的过程。如果可选项含有一个值,则属性、类函数或下标调用成功;如果可选项为nil,则属性、类函数或下标调用返回nil。可将多次查询链接起来,如果链接中的任何环节为nil,则整个链接失败。

注:Swift中的可选链接类似于在Objective-C中传送nil,但该链接适用于任何类型并且允许其检查成功或失败。

 

可选链接替代强制展开

 

通过将问号(?)置于可选值(当可选项为非nil时在该值上调用属性、类函数或下标)后即可指定可选链接。这与将叹号(!)置于可选值后以强制展开其值类似。主要不同之处在于,当可选项为nil时可选链接失败,但同时,如果可选项为nil,则强制展开将触发运行时错误。

为反映出可选链接可在nil值上被调用这一事实,可选链接调用结果始终为可选值,即使所查询的属性、类函数或下标返回一个非可选值。用户可使用此可选返回值来检查可选链接调用是成功(所返回的可选项含有一个值)或是由于链接中存在nil值而失败(所返回的可选值为nil)。

具体来说,可选链接调用的结果与预期返回值类型相同,但包含在可选项中。当通过可选链接访问时,通常情况下返回Int的属性将返回Int?。 以下几个代码片段说明了可选链接与强制展开有何不同以及可选链接如何为用户检查成功与否创造条件。

首先,名为Person和Residence两个类被定义为:

 

class Person {

var residence: Residence?

}

class Residence {

var numberOfRooms = 1

}

 

Residence实例具有一个单一的名为numberOfRooms的Int属性,默认值为1,Person实例具有一个类型为Residence?的可选residence属性。

如果用户创建一个新的Person实例,则凭借其可选性,默认为将其residence属性初始化为nil。在下面的代码中,john有一个值为零的residence属性:

 

let john = Person()

 

如果用户试图通过将叹号置于residence后以强制展开其值的方法来访问该住户residence的numberOfRooms属性,则会触发运行时错误,因为不存在residence值来展开:

 

let roomCount = john.residence!.numberOfRooms

// this triggers a runtime error

 

当john.resdence具有一个非nil值并且会将roomCount设为包含适当的房间数量的Int值时,则以上代码成功。但当residence为nil时,该代码总是会触发运行时错误-见上文介绍。

可选链接提供了另一种方式访问numberOfRooms值。若使用可选链接,需使用问号代替感叹号:

 

if let roomCount = john.residence?.numberOfRooms {

println("John's residence has \(roomCount) room(s).")

} else {

println("Unable to retrieve the number of rooms.")

}

// prints "Unable to retrieve the number of rooms."

 

这种情况使得Swift在可选residence属性上进行“链接”并检索出numberOfRooms的值(如果residence存在)。

由于访问numberOfRooms可能会失败,因此,可选链接会试图返回一个Int?类型的值或“可选Int”。当residence为nil时,如以上示例所示,此可选Int也将为nil,以反映出无法访问numberOfRooms这一事实。

注,即使numberOfRooms值为非可选Int类型,这也是真的。通过可选链接进行查询这一事实说明:对numberOfRooms的调用始终返回一个Int?而不是Int。

用户可将Residence实例分配给john.residence,以使其不再具有nil值:

 

john.residence = Residence()

 

john.residence现在包含一个实际Residence实例,而不是nil。如果用户试图以与上文相同的可选链接访问numberOfRooms,则此时其将返回一个Int?,其中包含numberOfRooms默认值1:

 

if let roomCount = john.residence?.numberOfRooms {

println("John's residence has \(roomCount) room(s).")

} else {

println("Unable to retrieve the number of rooms.")

}

// prints "John's residence has 1 room(s)."

 

 

定义可选链接的模型类

 

用户可在对深度等级大于一级的属性、类函数和下标进行调用时使用可选链接。这样,用户可对互联型复杂模型中的子属性进行深度查询,并可检查是否有可能访问属性、类函数及这些子属性上的下标。

以下代码片段定义了用于下文几个示例(包括多级可选链接示例)的四个模型类。这些类通过添加具有相关属性、类函数和下标的Room和Address类对上述Person和Residence模型进行了扩展。

按照之前的方式,定义Person类:

 

class Person {

var residence: 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提供了只读下标,该下标以以下声明为开始:传递至下标的索引为有效索引。如果索引有效,则下标将置于请求的rooms阵列索引中的room返回。

此版本的Residence还提供了一个名为printNumberOfRooms的方法,该方法仅打印出residence中rooms的数量。

最后,Residence定义了一个名为address的可选属性,其类型为Address?.此属性的Address类型定义如下。

用于rooms阵列的Room类是一个简单类,其中有一个名为name的属性及一个用于将该属性设为适当的room名称的初始化函数:

 

class Room {

let name: String

init(name: String) { self.name = name }

}

 

在这个模型中,最终类被称为Address.这个类有三个可选择的String?类型的属性前两个属性buildingName和buildingNumber是两种用于确认address中具体building的可选方法。第三个属性street被用来命名这个address 的 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类还提供了一个名为buildingIdentifer的方法,该方法具有一个String?返回类型。该方法检查buildingName和buildingNumber属性,并返回buildingName(如有值)或buildingNumber(如有值)或nil(如果这两个属性均没有值)。

 

通过可选链接调用属性

 

如强制展开的可替代性可选链接介绍,用户可使用可选链接访问可选值上的属性,并检查该属性访问是否成功。但是,你不能通过可选链接设置属性值。

使用上文定义的类来创建一个新Person实例,并以上文所述方式尝试访问其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.")

}

// prints "Unable to retrieve the number of rooms."

 

由于john.resaence为空,此可选链接调用以上述同样的方式失败,且未出现错误。

通过可选链接调用方法

 

用户可使用可选链接来调用可选值上的方法,并检查该方法调用是否成功。虽然该方法不能定义一个返回值,你仍可以这样做。

调用Residence类上的printNumberOfRooms方法打印出numberOfRooms的当前值。以下为此方法的展示:

 

func printNumberOfRooms() {

println("The number of rooms is \(numberOfRooms)")

}

 

此方法不能指定返回类型。但是,不带有返回类型的函数和方法具有一个隐式返回类型Void-见无返回值的函数下的介绍。

如果在一个带有可选链接的可选值上调用此方法,则该方法的返回类型将为Void?而不是Void,这是因为当通过可选链接进行调用时返回值始终为可选类型。这使得用户可使用if语句来检查是否有可能调用printNumberOfRooms方法,即使该方法本身并未定义一个返回值。如果通过可选链接成功调用该方法,则从printNumberOfRooms隐式返回的值将等于Void,否则该返回值为空。

 

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.")

}

// prints "It was not possible to print the number of rooms."

 

通过可选链接调用下标

 

用户可使用可选链接尝试检索可选值上的下标值,并检查该下标调用是否成功。但是,你不能通过可选链接设置下标值。

注:当通过可选链接访问可选值上的下标时,需要将问号置于下标的大括号前面而不是后面。可选链接问号始终紧跟在表达式可选部分之后。

以下示例尝试通过使用在Residence类中定义的下标来检索john.residence属性rooms阵列中第一个room名称。因为john.residence的当前值为空,所以下标调用失败:

 

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

println("The first room name is \(firstRoomName).")

} else {

println("Unable to retrieve the first room name.")

}

// prints "Unable to retrieve the first room name."

 

在这个下标调用中,可选链接问号紧随john.residence之后、下标括号之前,这是因为john.residence是可选链接试图在其上建立的可选值。

如果建立一个实际Residence实例并将其赋值给john.residence,并且rooms阵列中有一个或多个Room实例,则可使用Residence下标通过可选链接访问rooms阵列中的实际项:

 

let johnsHouse = Residence()

johnsHouse.rooms += Room(name: "Living Room")

johnsHouse.rooms += Room(name: "Kitchen")

john.residence = johnsHouse

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

println("The first room name is \(firstRoomName).")

} else {

println("Unable to retrieve the first room name.")

prints "The first room name is Living Room."

 

链接多层次链接

 

用户可将多个等级的可选链接联系起来,对处于模块中更深层次的属性、方法和下标进行深度查询。但是,多个等级的可选链接并不在所返回的值中添加更多等级的可选性。

以另一种角度说:

如果试图检索的类型不是可选性的,那么它会由于可选链接变为可选性的。

如果试图检索的类型已经是可选性的,那么它会由于可选链接变得更加有可选性。

因此:

如果试图通过可选链接来检索一个Int 值,不论用了多少层的链接,Int?始终是可返回的。

同样的,如果试图通过可选链接检索一个Int?值,不论用了多少层的链接,Innt?始终是可返回的。

以下示例试图访问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.")

}

// prints "Unable to retrieve the address."

 

目前john.resaence值含有一个有效的Residence实例。然而,john.residence.address值仍为空。正因为如此,john.resdence?.address?.street调用失败。

注:在以上示例中,用户试图检索street属性的值。此属性类型为String?因此,即使应用了两个级别的可选链接,除属性的底层可选类型外,john.resdence?.address?.street的返回值也还是String?。

如果将实际Address实例设为适用于john.street.address的值并为address的street属性设置一个实际值,则可通过多级可选链接访问属性值。

 

let johnsAddress = Address()

johnsAddress.buildingName = "The Larches"

johnsAddress.street = "Laurel Street"

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.")

prints "John's street name is Laurel Street."

 

在将address实例赋值给john.residence.address的过程中,注叹号的使用。john.residence属性具有一个可选类型,因此,在访问residence的address属性前,需要利用叹号展开其实际值。

 

链接方法和可选的返回值

 

上一示例说明了如何通过可选链接检索可选型属性的值。还可以使用可选链接来调用一个可返回可选类型值的方法,如果需要,还可在该方法的返回值上进行链接。

以下示例通过可选链接调用了Address类的buildingIdentifer方法。此方法可以返回一个String?类型的值如上所述,在进行可选链接后,此方法调用的最终返回类型值仍为String?:

 

if let buildingIdentifier = john.

residence?.address?.buildingIdentifier() {

println("John's building identifier is \(buildingIdentifier).")

}

// prints "John's building identifier is The Larches."

 

如果要在此方法的返回值上进行进一步的可选链接,应将可选链接问号置于该方法的括号后:

 

if let upper = john.residence?.address?.buildingIdentifier()?.uppercaseString {

println("John's uppercase building identifier is \(upper).")

}

// prints "John's uppercase building identifier is THE LARCHES."

 

注:在以上示例中,用户将可选链接问号置于括号后,这是因为所链接的可选值为buildingIdentifer方法的返回值而不是该方法本身。

昵    称:
验证码:

相关文档:

swift
IOS实例
ObjectiveC