标签:
其实可空链式调用并没有它的名字那么陌生,简而言之就是对于可选类型Optional
(使用问号 ? 后缀表示)和强制展开类型(使用感叹号 ! 后缀表示)的使用方法。在平常写代码的时候只是大概的清楚哪些值是可空的,哪些值是肯定存在的,但是并没有深究可空的调用有何优点,有何使用时需要注意的事项。至少前面写不少示例代码的时候,我也是大都按照自己的想法去定义的。这一小节就是对可空调用的详细描述,至于链式,就是多层调用,大概就是school.classroom.student.name
这样的调用方法。
小节知识点,看着挺多挺长,但是如果把可空链式调用
这几个忽略掉,就会发现都是我们已经非常熟悉的基本用法:
可空链式调用是一种可以请求和调用属性、方法以及下标的过程,它的可空性体现在请求和调用的目前当前可能为空nil
。如果可空的目标有值,那么就会调用成功;如果选择的目标为空,那么这种调用将会返回空nil
。多个连续的调用可以被链接到一起形成一个调用链,如果其中任何一个节点为空nil
,将会导致整个调用失败。
Swift的可空链式调用和Objective-C中的消息为空有点像,但是Swift可以使用在任何类型中,并且能够检查调用是否成功。一个可空调用相当于Objective-C中if (message == nil) { } else { }
,只是Objective-C中只能用在对象或者变量中,远没有Swift中功能强大。
使用可空链式调用来强制展开
下面这几句话读起来有点拗口,多理解就好:通过在想调用非空的属性、方法、或下标的可空值optional value
后面放置一个问号 ?
,可以定义一个可空链。这一点很像在一个可空值后面放一个感叹号 !
来强制展开其中值。它们的主要区别在于当可空值为空时可空链式只是调用失败,然而强制揭开将会出发运行错误。
简单来说,就是当调用一个属性或方法时,我们希望它是非空的,是有值的,但是这个我们并不能确定有值,就会用到可空链。譬如获取网络数据,然后展示,我们当然希望是获取到数据的,但是有时候因为网络、后台的问题,数据为空,此时如果用可空链来处理,就会简单些。
需要强调的是,可空链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了一个可空类型值。例如,当可空链式调用成功时,一个int
类型的结果将会返回int?
类型。
下面几行示例代码展示可空链式调用和强制展开的一些不同点:
// 户主信息
class Person {
var residence: Residence?
}
// 房子信息,一共有多少房间
class Residence {
var numberOfRooms = 1 // 默认只有一个房间
}
// 创建一个新的 Person 实例
let personOne = Person()
// 由于 residence 属性为空,所以下面如果将 ? 换成 ! 强制解开,会报错。另外,这里代码补全就是带 ? 的。
print(personOne.residence?.numberOfRooms) // 输出: nil
// 初始化 residence 属性
personOne.residence = Residence()
// 此时 residence 已经有了初始值,但是胆码补全的时候还是用了 ? ,此时如果将 ? 改为 ! , 并不会报错,因为是对一个已经确定的非空值强制展开
print(personOne.residence!.numberOfRooms) // 输出: 1
print(personOne.residence?.numberOfRooms) // 输出: Optional(1) , 表示可空的
其实可空链式调用最常用的一个场景就是if
语句的判断,当可空链式调用返回是nil
时执行操作A,不是nil
时执行操作B,如下:
// 可空链式调用用于 if 语句判断
if let person = personOne.residence?.numberOfRooms {
// 此时不为空 personOne.residence?.numberOfRooms = 1
} else {
// 此时为空 personOne.residence?.numberOfRooms = nil
}
通过可空链式调用定义模型类
通过使用可空链式调用可以调用多层属性、方法和下标。这样可以通过各种模型向下访问各种子属性。并且可以根据是否为空判断是否可以访问子属性的属性、方法或下标。
下面示例代码定义了四个模型类,这些例子包括多层可空链式调用。同时,下面几个知识点也会以这4个类为基础说明:
// 户主信息
class Person {
// 房子信息,可能没有房子,所以可选比较合适
var residence: Residence?
}
// 房子信息
class Residence {
// 房间数组
var rooms = [Room]()
// 一共有多少房间
var numberOfRooms: Int {
return rooms.count
}
// 房子地址
var address: Address?
// 下标方法,选择第几间房
subscript(index: Int) -> Room {
get {
return rooms[index] // 取值
}
set {
rooms[index] = newValue // 设置值
}
}
// 打印房间数量
func printNumberOfRooms() {
print("number of rooms : \(rooms.count)")
}
}
// 房间信息
class Room {
// 房间名字
let name: String
init(roomName name: String) {
self.name = name
}
}
// 地址信息
class Address {
var buildingName: String? // 建筑名字
var buildingNumber: String? // 建筑编号
var buildingStreet: String? // 建筑所在街道名称
// 方法,获取建筑物的唯一标示符,因为可能为空,所以返回的String类型是可选的
func buildingIdentifier() -> String? {
if let name = buildingName {
return name
} else if let number = buildingNumber {
return number
} else {
return nil
}
}
}
通过可空链式调用访问属性
就是我们平常调用属性、方法的那一套,先创建一个实例,然后去调用。其实,很多时候实例、属性等是可空的还是强制展开的,系统都会帮我们去辨别的,我们需要去理解为什么会出现补全的情况,有时候需要我们手动去决定可选还是强制展开。
下面是代码示例,主要注意点都写在注释里了:
// 创建一个户主实例
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
// 可空链式调用成功,这里不会执行,因为 john.residence?.numberOfRooms 此时是 nil
} else {
// 可空链式调用失败,会执行这里的代码
}
// 通过可空链式调用设置属性值
john.residence = Residence() // 首先实例化属性 john.residence
john.residence?.address = Address() // 实例化地址
// 此时,address 已经存在,才能修改它的属性值
john.residence?.address?.buildingName = "北京"
print(john.residence?.address?.buildingIdentifier()) // 输出: Optional("北京")
print(john.residence!.address!.buildingIdentifier()!) // 输出:北京
/*************** 上面的代码必须每一个属性都是强制展开的,任何一个可空时,都会输出 Optional("北京") ***************/
通过可空链式调用来调用方法
可以通过可空链式调用来调用方法,并判断是否调用成功。即使这个方法没有返回值。其实没有返回值的方法隐式的返回Void
类型。这意味着没有返回值的方法也会返回 ()
或者空的元组。
如果在可空值上通过可空链式调用来调用没有返回值的方法,那么返回的类型将会是可选空类型 Void?
,而不是 Void
。因为空过可空链式调用得到的返回值都是可空 nil
的。
下面几行示例代码结合上面的定义的数据类型,展示出可空返回值可返回空元组的方法的区别:
// 不在可空链式调用上调用没有返回值的方法,默认是 Void 类型的返回,返回一个空的元组
let residence = Residence()
print(residence.printNumberOfRooms()) // 输出:number of rooms : 0 ()
// 在可空链式调用上调用没有返回值的方法,返回的是一个可选的空类型 Void? ,因为只有可选类型才能为 nil
let tom = Person()
print(tom.residence?.printNumberOfRooms()) // 输出: nil
同样的,可以判断通过可空链式调用来给属性赋值是否成功。首先,需要知道的是无法给一个 nil
的属性赋值:
let jack = Person()
let jackResidence = Residence()
jack.residence? = jackResidence // 这句话并没有给 jack.residence? 赋值
// 此时 jack.residence? == nil
if (jack.residence? = jackResidence) != nil{
print("赋值成功")
} else {
print("赋值失败")
}
// 输出: 赋值失败
作为对比,对比下面这段代码和上面的区别:
let jack = Person()
let jackResidence = Residence()
jack.residence = jackResidence // 这句话给 jack.residence 了内存地址
// 此时 jack.residence 已经被实例了
if (jack.residence? = jackResidence) != nil{
print("赋值成功")
} else {
print("赋值失败")
}
// 输出: 赋值成功
通过可空链式调用来访问下标
通过可空链式调用,可以用下标来对空值进行读取或写入,并且判断下标是否调用成功。注意:当通过可空链式调用访问可空值得下标的时候,应该将问号放在下标方括号的前面而不是后面。可空链式调用的问号一般直接跟在可控表达式的后面。问号跟在哪个表达式的后面表示哪个表达式是可控的。
其实访问下标和访问属性、方法并没有什么不同,可用于 if
语句判断,不能给一个值为 nil
的属性赋值:
// 访问下标
let rose = Person()
if let firstRoomName = rose.residence?.rooms[0].name {
print("因为 rose.residence? 是可空值,并且此时值为 nil, 所以这句话并不会被打印") // 不执行
} else {
print("被打印的话") // 执行
}
// 给一个值为 nil 的属性赋值
rose.residence?[0] = Room(roomName: "Living Room") // 赋值失败,此时 rose.residence?[0] == nil
rose.residence = Residence() // 创建实例
rose.residence?.rooms.append(Room(roomName: "FirstRoom")) // 添加值
// 再次尝试访问下标
if let firstRoomNameAgain = rose.residence?.rooms[0].name {
print("因为 rose.residence? 是可空值,并且此时值为 nil, 所以这句话并不会被打印") // 执行
} else {
print("被打印的话") // 不执行
}
如果下标返回可空类型值,比如 Swift 中 Dictionary
的 key
下标。可以在下标的闭合括号后面放一个问号来链接下标的可空返回值:
var testScrore = ["john": [88, 91, 78], "jack": [99, 112, 130]]
testScrore["john"]?[0] = 140
testScrore["jack"]?[0] = 33
testScrore["Hehe"]?[0] = 44 // 这句并不会执行,去掉问号会报错
print(testScrore) // 输出: ["jack": [33, 112, 130], "john": [140, 91, 78]]
多层链接
可以通过多个属性的可空链式调用来向下访问属性,方法以及下标。但是多层可空链式调用不会添加返回值的可空性,也就是说:
因此:
Int
值,将会返回 Int?
,不论进行了多少次可空链式调用。Int?
值,并不会变得更空。对返回可空的函数进行链接
上面的例子说明了如何通过可空链式调用来获取可控的属性值。其实还可以通过可空链式调用来调用返回值是可空值的方法,并且可以继续对空值进行链接:
// 对返回值可空的方法可以继续往下链接
let liLei = Person()
// 注意,在方法的圆括号后面加问号并不是指方法可空,而是指方法的返回值可空。
if let beginWithPrefix = liLei.residence?.address?.buildingIdentifier()?.hasPrefix("China") {
} else {
}
这一小节其实是有点唬人的,它只是把我们平常一直用但是没有太在意的事情说了一遍,所以学起来也是挺轻松的。
我人很懒的,一直学习什么的也是办不到。这两天抽空找了一部动漫看看,绝园的暴风雨,整部动漫围绕莎士比亚的哈姆雷特、暴风雨两个复仇剧展开,显然这么高大上的书我是没读过的,但是动漫本身相当不错,角色都很有特点。喜欢看动漫的可以补一下。
Swift-可空链式调用(Optional Chaining)(十五)
标签:
原文地址:http://blog.csdn.net/xxh0307/article/details/51516440