15-Swift可空鏈?zhǔn)秸{(diào)用

可空鏈?zhǔn)秸{(diào)用是一種可以請(qǐng)求和調(diào)用屬性、方法以及下標(biāo)的過(guò)程,它的可空體現(xiàn)在請(qǐng)求或調(diào)用的目標(biāo)當(dāng)前可能為nil。如果可空的目標(biāo)有值,即調(diào)用就會(huì)成功;如果選擇的目標(biāo)為nil,即調(diào)用將返回nil。多個(gè)連續(xù)的調(diào)用可以被鏈接在一起形成一個(gè)調(diào)用鏈,如果其中任何一個(gè)節(jié)點(diǎn)為nil將導(dǎo)致整個(gè)鏈調(diào)用失敗。


一、使用可空鏈?zhǔn)秸{(diào)用來(lái)強(qiáng)制展開(kāi)


可空鏈的定義,即是在要調(diào)用非空的屬性、方法、下標(biāo)的可空值后面添加一個(gè)問(wèn)號(hào)即可。特別的,可空鏈?zhǔn)秸{(diào)用的返回結(jié)果與原本的返回結(jié)果具有相同的類型,但是被包裝成了一個(gè)可空類型值,當(dāng)可空鏈?zhǔn)秸{(diào)用成功時(shí),一個(gè)本該返回Int類型的結(jié)果將返回Int?類型。

class Person {
    // 可空屬性,類型為Student?
    var student:Student?
}

class Student {
    var name = "xxx"
}

let zhangsan = Person()
// 這是錯(cuò)誤的,因?yàn)閦hangsan.student現(xiàn)在是為`nil`
//let name = zhangsan.student!.name

// 可空鏈?zhǔn)秸{(diào)用,即是使用`?`來(lái)代替`!`
// 即是說(shuō),只有當(dāng)student不為空的時(shí)候才訪問(wèn)name
let name1 = zhangsan.student?.name
// 另外`var student:Student?`,如果student是為`nil`,那么返回的name也是可空類型的,雖然在定義的時(shí)是`var name = "xxx"`
if name1 == nil {
    print("zhangsan --- Student沒(méi)有進(jìn)行實(shí)例化,即沒(méi)有名字")
} else {
    print("zhangsan --- \(name1)")
}

let lisi = Person()
// 注意,只要有作實(shí)例化操作,那么lisi.student就不是為`nil`
lisi.student = Student()
let name2 = lisi.student?.name
if name2 == nil {
    print("lisi --- Student沒(méi)有進(jìn)行實(shí)例化,即沒(méi)有名字")
} else {
    print("lisi --- \(name2!)")
輸出結(jié)果:
zhangsan --- Student沒(méi)有進(jìn)行實(shí)例化,即沒(méi)有名字
lisi --- xxx


二、為可空鏈?zhǔn)秸{(diào)用定義模型類


通過(guò)使用可空鏈?zhǔn)秸{(diào)用可以調(diào)用多層屬性、方法、下標(biāo)。這可以通過(guò)各種模型向下訪問(wèn)各種子屬性,并且判斷能否訪問(wèn)子屬性中的屬性、方法、下標(biāo)。

// 人類
class Person {
    // 一個(gè)人,不一定就有房子,即是可空的
    var residence:Residence?
}

// 房子類
class Residence {
    // 房間數(shù)組
    var rooms = [Room]()
    // 房間個(gè)數(shù)
    var numberOfRoos:Int {
        return rooms.count
    }
    // 下標(biāo)腳本,下標(biāo)快捷訪問(wèn)數(shù)組rooms
    subscript(i:Int) -> Room {
        get {
            return rooms[i]
        }
        set {
            rooms[i] = newValue
        }
    }
    
    // 打印房子中房間數(shù)量函數(shù)
    func printNumberOfRooms() {
        print("這個(gè)房子有\(zhòng)(numberOfRoos)個(gè)房間")
    }
}

// 房間類
class Room {
    let name:String
    init(name:String) {
        self.name = name
    }
}

// 地址類
class Address {
    // 房子建造者的名字,可空屬性
    var buildingName:String?
    // 房子建造者的聯(lián)系方法,可空屬性
    var buildingTell:String?
    // 房子所在位置,可空屬性
    var street:String?
    
    // 房子建造的相關(guān)信息
    func buildingIdentifier() -> String? {
        if buildingName != nil {
            return buildingName
        } else if buildingTell != nil {
            return buildingTell
        } else {
            return nil
        }
    }
}


三、通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)訪問(wèn)屬性


根據(jù)上面創(chuàng)建的類,實(shí)例操作來(lái)進(jìn)行訪問(wèn)對(duì)應(yīng)屬性:

// 實(shí)例化Person
let endEvent = Person()
// 注意Residence為`nil`,那么可空鏈?zhǔn)秸{(diào)用失敗
if let roomCount = endEvent.residence?.numberOfRoos {
    print("EndEvent房子中有\(zhòng)(roomCount)個(gè)房間")
} else {
    print("Residence沒(méi)有進(jìn)行實(shí)例化操作")
}

// 實(shí)例化Address
let guangzhou = Address()
guangzhou.buildingName = "xxx"
guangzhou.street = "體育中心"
guangzhou.buildingTell = "xxx-xxx-xxx"
// 通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)設(shè)置屬性值
// 但是此時(shí)設(shè)置是失敗的,因?yàn)閑ndEvent.residence為空
endEvent.residence?.address = guangzhou
// 打印是為`nil`
print(endEvent.residence?.address?.street)
輸出結(jié)果:
Residence沒(méi)有進(jìn)行實(shí)例化操作
nil

從上可以看出,使用可空鏈?zhǔn)秸{(diào)用的好處,在于雖然endEvent.residencenil,在設(shè)置address屬性時(shí)程序不會(huì)崩潰,只是設(shè)置會(huì)失敗。


四、通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)調(diào)用方法


Residence類中,有一個(gè)printNumberOfRooms方法,是打印房子中房間數(shù)量的函數(shù)。對(duì)于這個(gè)函數(shù),其返回值類型是Void。但是如果在可空值上,通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)調(diào)用此方法,返回值類型是Void?,而不是Void,因?yàn)橥ㄟ^(guò)可空鏈?zhǔn)秸{(diào)用得到的返回值是nil

// 如果有實(shí)例操作,那么就會(huì)是調(diào)用成功,否則是調(diào)用失敗
//endEvent.residence = Residence()

// 通過(guò)返回值是否為`nil`可以判斷調(diào)用是否成功
if endEvent.residence?.printNumberOfRooms() != nil {
    print("調(diào)用成功")
} else {
    print("調(diào)用失敗")
}


五、通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)訪問(wèn)下標(biāo)


通過(guò)可空鏈?zhǔn)秸{(diào)用,可以用下標(biāo)來(lái)對(duì)可空值進(jìn)行讀寫(xiě),并可以判斷下標(biāo)調(diào)用是否成功。

注意: 當(dāng)通過(guò)可空鏈?zhǔn)秸{(diào)用訪問(wèn)可空值的下標(biāo)時(shí),應(yīng)該將問(wèn)號(hào)放在下標(biāo)方括號(hào)的前面而不是后面??煽真?zhǔn)秸{(diào)用的問(wèn)號(hào)一般直接跟在可空表達(dá)式的后面。

endEvent.residence = Residence()
// 追加操作
endEvent.residence?.rooms.append(Room(name: "房間1"))
endEvent.residence?.rooms.append(Room(name: "房間2"))
endEvent.residence?.rooms.append(Room(name: "房間3"))
// 通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)訪問(wèn)下標(biāo)
if let firstRoomName = endEvent.residence?[0].name {
    print("第一個(gè)房間的名字是\(firstRoomName)")
} else {
    print("沒(méi)有對(duì)應(yīng)房間")
}
輸出結(jié)果:
第一個(gè)房間的名字:房間1
// 訪問(wèn)可空類型的下標(biāo)
// 字典類型
var dict = ["name":["張三","李四","王五"],
            "age":[18, 20, 23]]

// 用可空鏈?zhǔn)秸{(diào)用,把"name"數(shù)組第一個(gè)元素設(shè)置為"趙六"
dict["name"]?[0] = "趙六"

// 將"age"數(shù)組第一個(gè)元素設(shè)置為25
dict["age"]?[0] = 25

// 將"test"數(shù)組第一個(gè)元素設(shè)置為"測(cè)試",但是字典中不存在關(guān)鍵字為"test"的,所以這個(gè)是調(diào)用失敗
dict["test"]?[0] = "測(cè)試"

print(dict)
輸出結(jié)果:
["age": [25, 20, 23], "name": [趙六, 李四, 王五]]


六、多層鏈接


可以通過(guò)多個(gè)鏈接多個(gè)可空鏈?zhǔn)秸{(diào)用來(lái)向下訪問(wèn)屬性、方法、下標(biāo)。但是多層可空鏈?zhǔn)秸{(diào)用不會(huì)添加返回值的可空性,即是:
?- 如果訪問(wèn)的值不是可空的,通過(guò)可空鏈?zhǔn)秸{(diào)用將會(huì)可以放可空值。
?- 如果訪問(wèn)的值已經(jīng)是可空的,通過(guò)可空鏈?zhǔn)秸{(diào)用不會(huì)變得"更"可空。

// 多層可空鏈?zhǔn)秸{(diào)用
let endEvent = Person()
// 實(shí)例化Residence(如果沒(méi)有下實(shí)例化,那么可空鏈?zhǔn)綄?huì)調(diào)用失敗)
endEvent.residence = Residence()
// 實(shí)例化Address
let guangzhou = Address()
guangzhou.buildingName = "xxx"
guangzhou.street = "廣州 -- 體育中心"
guangzhou.buildingTell = "xxx-xxx-xxx"
endEvent.residence?.address = guangzhou

// `street`屬性是`String?`,endEvent.residence?.address?.street返回值是`String?`,即是已經(jīng)進(jìn)行了兩次可空鏈?zhǔn)秸{(diào)用
if let myStreet = endEvent.residence?.address?.street {
    print("endEvent的住房地址: \(myStreet)")
} else {
    print("endEvent沒(méi)有房子")
}
輸出結(jié)果:
endEvent的住房地址: 廣州 -- 體育中心


七、對(duì)返回可空值的函數(shù)進(jìn)行鏈接


除了通過(guò)可空鏈?zhǔn)秸{(diào)用獲取可空屬性值,也還可以通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)調(diào)用返回值為可空的方法。

// 通過(guò)可空鏈?zhǔn)秸{(diào)用來(lái)調(diào)用`Address`中的`buildingIdentifier()`方法,返回類型是`String?`。
  if let myBuildingIdentifier = endEvent.residence?.address?.buildingIdentifier() {
    print("建造信息: \(myBuildingIdentifier)")
}

注意: 在方法的括號(hào)后面加上問(wèn)號(hào),是因?yàn)?code>buildingIdentifier()返回值是可空值,而不是方法本身是可空的?。?!


注:xcode7.3環(huán)境

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 可選鏈?zhǔn)秸{(diào)用 是一種可以在當(dāng)前值可能為 nil 的可選值上請(qǐng)求和調(diào)用屬性、方法及下標(biāo)的方法。如果可選值有值,那么調(diào)...
    莽原奔馬668閱讀 393評(píng)論 0 2
  • 本章將會(huì)介紹 自動(dòng)引用計(jì)數(shù)的工作機(jī)制自動(dòng)引用計(jì)數(shù)實(shí)踐類實(shí)例之間的循環(huán)強(qiáng)引用解決實(shí)例之間的循環(huán)強(qiáng)引用閉包引起的循環(huán)強(qiáng)...
    寒橋閱讀 1,052評(píng)論 0 0
  • 官方文檔鏈接 原文鏈接 可選鏈(Optional Chaining)是為了在一個(gè)可能當(dāng)前值為nil的optiona...
    hrscy閱讀 414評(píng)論 0 3
  • 基礎(chǔ)部分(The Basics) 當(dāng)推斷浮點(diǎn)數(shù)的類型時(shí),Swift 總是會(huì)選擇Double而不是Float。 結(jié)合...
    gamper閱讀 1,509評(píng)論 0 7
  • 二零一七年四月四日,讀《世說(shuō)新語(yǔ)》時(shí)看到「清言」一詞,于是有了如下對(duì)話。 平蕪:清談,怎么個(gè)談法?談道學(xué)么,還是談...
    平蕪君閱讀 177評(píng)論 0 0

友情鏈接更多精彩內(nèi)容