可空链式调用(Optional Chaining)是一种可以请求和调用属性、方法及下标的过程,它的可空性体现于请求或调用的目标当前可能为空(nil)。如果可空的目标有值,那么调用就会成功;如果选择的目标为空(nil),那么这种调用将返回空(nil)。多个连续的调用可以被链接在一起形成一个调用链,如果其中任何一个节点为空(nil)将导致整个链调用失败。
1:可选链存在的理由 为什么swift要引入可选链的概念,假设我们现在有人,宠物和宠物玩具3类.
//定义人类
class Person{
var pet: Pet? //并不是人人都喜欢宠物,所以定义为可选
}
//定义宠物类
class Pet{
var name: String
var favoriteToy: Toy? //不是所有的宠物都爱玩具
init(name: String){
self.name = name
}
}
//定义宠物的玩具类
class Toy{
var name: String
init(name: String){
self.name = name
}
}
func testone(){
let john = Person()
//强制解封 john中的可选属性pet,会cash.
print("\(john.pet!.favoriteToy!.name)")
//fatal error: unexpectedly found nil while unwrapping an Optional value
//注意:我们定义了john实例,并且强制解封地访问它的宠物玩具的名称,可是此时他并没有宠物,所以程序崩掉了.那我们应该如何做来防止上面的错误呢?修改代码看一看:
}
//修改代码如下:
func testone(){
let john = Person()
//强制解封 john中的可选属性pet,会cash.
//print("\(john.pet!.favoriteToy!.name)")
//fatal error: unexpectedly found nil while unwrapping an Optional value
//改成嵌套形式进行判断
if let pet = john.pet{
if let toy = pet.favoriteToy{
print("该宠物喜欢的玩具是\(toy.name)")
}
}
//程序运行没有问题,但是如果判断条件很多,那么需要大量的if语句,这样不方面,所以Swift为我们提供了可选链如下:
if let someToy = john.pet?.favoriteToy?.name{
print("该主人的宠物喜欢\(someToy)")
}else{
print("该主人没有宠物")
}
//运行结果如下:该主人没有宠物
//if let someToy = john.pet?.favoriteToy?.name 就是可选链,可选链的格式为:串联要访问的属性并且在每个可选后面增加?(问号).优点在于:可以使用一层 if语句代替多层 if语句,直接访问我们要使用的属性,方法,附属脚本等.
}
2:可选链的4种场景
首先优化之前的类,添加相应的代码如下:
//定义人类
class Person{
var pet: Pet? //并不是人人都喜欢宠物,所以定义为可选
}
//定义宠物类
class Pet{
var name: String
//定义玩具集合
var toys: [Toy] = [Toy]()
//计算属性,返回宠物玩具的个数
var numberOfToys:Int{
return toys.count
}
//不是所有的宠物都爱玩具
var favoriteToy: Toy?{
if numberOfToys != 0{
return toys[0]
}else{
return nil
}
}
//访问指定序号的玩具
subscript(i: Int) -> Toy{
get{
return toys[i]
}
set{
toys[i] = newValue
}
}
init(name: String){
self.name = name
}
//打印玩具数量
func printNumberOfToys(){
print("玩家的个数:\(numberOfToys)")
}
}
//定义宠物的玩具类
class Toy{
var name: String
//玩具原产地
var placeOfOriginal: String?
init(name: String){
self.name = name
}
//获取玩具原产地
func getPlaceOfOriginal() ->String?{
if placeOfOriginal != nil{
return placeOfOriginal
}else{
return nil
}
}
}
场景一 通过可选链访问属性
func testTwo(){
let john = Person()
if let toysNumber = john.pet?.numberOfToys{//注意:问号只能够打在可选类型上.
print("john的宠物有\(toysNumber)个玩具")
}else{
print("john没有宠物,没法得知玩具的数量!")
}
//john没有宠物,没法得知玩具的数量!
//因为Pet类的实例还没有被创建,所以john.pet的值为nil,当我们尝试用可选链去解封john.pet时,由于john.pet的值为nil,所以导致整个可选链表达式失败,从而返回nil.
//可选链设置值.
if(john.pet?.name = "snow") != nil{
print("赋值成功!")
}else{
print("赋值失败!")
}
//赋值失败! 因为john.pet的值为nil,所以在进行赋值操作的时候要注意,可能赋值失败,但是放心,如果我们赋值失败, Swift会返回一个Void?的可选类型,所以我们可以判断值是否为nil来判断赋值是否成功!
}
场景二 通过可选链访问方法
func testThree(){
let john = Person()
//使用可选链检査是否能够调用Pet类的printNumberOfToys方法.由于printNumberOfToys返回 void,所以该可选链返回 void?类型,又由于 john.pet为空,所以整个可选链返回值为nil.
if john.pet?.printNumberOfToys() != nil{
print("可以打印宠物数量!")
}else{
print("无法打印宠物数量!")
}
//无法打印宠物数量!
}
场景三 通过可选链访问下标
func testFour(){
let john = Person()
//注意:这里的可选是要john.pet之后,而不是john.pet[0]之后,因为pet才是可选类型.
if let favoriteToyName = john.pet?[0].name{//首先査询可选类型john的 pet属性有没有值,如果有值,则继续访问其下标,并通过下标获取对应 Toy实例中 name的值.
print("john的宠物最喜欢的玩具是\(favoriteToyName)")
}else{
print("无法获取宠物最喜欢的玩具!")
}
//无法获取宠物最喜欢的玩具!
//可选链访问下标,设置新玩具
if (john.pet?[0].name == "魔方") != nil{
print("无法给john的宠物换玩具!")
}
//无法给john的宠物换玩具!
//创建pet实例对象
john.pet = Pet(name: "Snow")
john.pet!.toys.append(Toy(name: "魔方"))
if let toyName = john.pet?[0].name{
print("john的宠物最喜欢的玩具是\(toyName)")
}else{
print("无法获取宠物最喜欢的玩具!")
}
//john的宠物最喜欢的玩具是魔方
}
场景四 通过可选链访问方法的返回值
//规则:当一个方法的返回类型为一个可选类型时,可选链可以继续访问返回值中的嵌套值.
func testFive(){
let john = Person()
john.pet = Pet(name: "Snow")
let toy = Toy(name: "魔方")
toy.placeOfOriginal = "China"
john.pet!.toys.append(toy)
if let isBeginWithC = john.pet?.toys[0].getPlaceOfOriginal()?.hasPrefix("C"){
if isBeginWithC{
print("玩具\(toy.name)是\(toy.placeOfOriginal!)制造!")
}else{
print("无法获得产地名!")
}
}
//结果:玩具魔方是China制造!
//上面代码部分可选链?打在getPlaceOfOriginal()方法之后,表明我们通过可选链査询该方法的返回值类型最终有没有值,该方法返回String?,然后再使用String的hasPrefix方法
}