Day13 可選鏈式調(diào)用

可選鏈式調(diào)用是一種可以在當前值可能為 nil 的可選值上請求和調(diào)用屬性、方法及下標的方法礼旅。
如果可選值有值,那么調(diào)用就會成功;如果可選值是 nil ,那么調(diào)用將返回 nil 。
多個調(diào)用可以連接在一起形成一個調(diào)用鏈公给,如果其中任何一個節(jié)點為 nil ,整個調(diào)用鏈都會失敗蜘渣,即返回 nil 淌铐。

1、使用可選鏈式調(diào)用代替強制展開

下面幾段代碼將解釋可選鏈式調(diào)用和強制展開的不同:
首先定義兩個類 Person 和 Residence :

    class Person {
        var residence: Residence?
    }
    class Residence {
        var numberOfRooms = 1
    }
     let john = Person()

如果使用嘆號( ! )強制展開獲得這個 john 的 residence 屬性中的 numberOfRooms 值蔫缸,會觸發(fā)運行時錯誤腿准,因為這時 residence 沒有可以展開的值:

let roomCount = john.residence!.numberOfRooms // 這會引發(fā)運行時錯誤

john.residence 為非 nil 值的時候,上面的調(diào)用會成功捂龄,并且把 roomCount 設(shè)置為 Int 類型的房間數(shù)量释涛。正如上 面提到的,當 residence 為 nil 的時候上面這段代碼會觸發(fā)運行時錯誤倦沧。

    //可選鏈式調(diào)用提供了另一種訪問 numberOfRooms 的方式唇撬,使用問號( ? )來替代原來的嘆號( ! ):
    
    if let roomCount = john.residence?.numberOfRooms {
        print("John's residence has \(roomCount) room(s).")
    } else {
        print("Unable to retrieve the number of rooms.")
    }
    // 打印 “Unable to retrieve the number of rooms.”

要注意的是,即使 numberOfRooms 是非可選的 Int 時展融,這一點也成立窖认。只要使用可選鏈式調(diào)用就意味著numberOfRooms 會返回一個 Int? 而不是 Int 。

   // 可以將一個 Residence 的實例賦給 john.residence ,這樣它就不再是 nil 了:
    john.residence = Residence()
    
    if let roomCount = john.residence?.numberOfRooms {
        print("John's residence has \(roomCount) room(s).")
    } else {
        print("Unable to retrieve the number of rooms.")
    }
    // 打印 “John's residence has 1 room(s).”
    */

2扑浸、為可選鏈式調(diào)用定義模型類

通過使用可選鏈式調(diào)用可以調(diào)用多層屬性烧给、方法和下標。這樣可以在復(fù)雜的模型中向下訪問各種子屬性喝噪,并且判斷能否訪問子屬性的屬性础嫡、方法或下標。

eg:

    class Person {
        var residence: Residence?
    }
    class Residence {
        var rooms = [Room]()//增加了一個名為 rooms 的變量屬性酝惧,該屬性被初始化為 [Room] 類型的空數(shù)組:
        var numberOfRooms: Int {
            return rooms.count
        }
        subscript(i: Int) -> Room {
            get {
                return rooms[i]
            }
            set {
                rooms[i] = newValue
            }
        }
        func printNumberOfRooms() {
            print("The number of rooms is \(numberOfRooms)")
        }
        var address: Address?
    }
    
    class Room {
        let name: String
        init(name: String) { self.name = name }
    }
    
    class Address {
        var buildingName: String?
        var buildingNumber: String?
        var street: String?
        func buildingIdentifier() -> String? {
            if buildingName != nil {
                return buildingName
            } else if buildingNumber != nil && street != nil {
                return "\(buildingNumber) \(street)"
            } else {
                return nil
            }
        }
    }

3榴鼎、通過可選鏈式調(diào)用訪問屬性

可以通過可選鏈式調(diào)用在一個可選值上訪問它的屬性,并判斷訪問是否成功晚唇。
下面的代碼創(chuàng)建了一個 Person 實例巫财,然后像之前一樣,嘗試訪問 numberOfRooms 屬性:

     let john = Person()
    //因為 john.residence 為 nil 哩陕,所以這個可選鏈式調(diào)用依舊會像先前一樣失敗平项。
    if let roomCount = john.residence?.numberOfRooms {
        print("John's residence has \(roomCount) room(s).")
    } else {
        print("Unable to retrieve the number of rooms.")
    }
    // 打印 “Unable to retrieve the number of rooms.”

還可以通過可選鏈式調(diào)用來設(shè)置屬性值:
eg:

    let someAddress = Address()
    someAddress.buildingNumber = "29"
    someAddress.street = "Acacia Road"
    john.residence?.address = someAddress
    print("address:\(john.residence?.address)")//nil
    //在這個例子中,通過 john.residence 來設(shè)定 address 屬性也會失敗悍及,因為 john.residence 當前為 nil 闽瓢。

下面的代碼完成了同樣的事情,但是它使用一個函數(shù)來創(chuàng)建 Address 實例并鸵,然后將該實例返回用于賦值鸳粉。
該函數(shù)會在返回前打印“Funct ion was called”,這使你能驗證等號右側(cè)的代碼是否被執(zhí)行园担。

    func createAddress() -> Address {
        print("Function was called.")
        let someAddress = Address()
        someAddress.buildingNumber = "29"
        someAddress.street = "Acacia Road"
        return someAddress
    }
    
    john.residence?.address = createAddress()//驗證等號右側(cè)的代碼是否被執(zhí)行届谈。
    
    //沒有任何打印消息,可以看出 createAddress() 函數(shù)并未被執(zhí)行弯汰。

4艰山、通過可選鏈式調(diào)用調(diào)用方法

    //可以通過可選鏈式調(diào)用來調(diào)用方法,并判斷是否調(diào)用成功咏闪,即使這個方法沒有返回值曙搬。
    
    //通過判斷返回值是否為 nil 可以判斷調(diào)用是否成功:
    if john.residence?.printNumberOfRooms() != nil {
        print("It was possible to print the number of rooms.")
    } else {
        print("It was not possible to print the number of rooms.")
    }
    // 打印 “It was not possible to print the number of rooms.”
    
    //同樣的,可以據(jù)此判斷通過可選鏈式調(diào)用為屬性賦值是否成功
    
    if (john.residence?.address = someAddress) != nil {
        print("It was possible to set the address.")
    } else {
        print("It was not possible to set the address.")
    }
    // 打印 “It was not possible to set the address.”

5鸽嫂、通過可選鏈式調(diào)用訪問下標

通過可選鏈式調(diào)用纵装,我們可以在一個可選值上訪問下標,并且判斷下標調(diào)用是否成功据某。

注意:
通過可選鏈式調(diào)用訪問可選值的下標時橡娄,應(yīng)該將問號放在下標方括號的前面而不是后面⊙⒆眩可選鏈式調(diào)用的問號一 般直接跟在可選表達式的后面挽唉。
eg:

    if let firstRoomName = john.residence?[0].name {
        print("The first room name is \(firstRoomName).")
    } else {
        print("Unable to retrieve the first room name.")
    }
    // 打印 “Unable to retrieve the first room name.”
    
    //類似的滤祖,可以通過下標,用可選鏈式調(diào)用來賦值:
    john.residence?[0] = Room(name: "Bathroom")//這次賦值同樣會失敗瓶籽,因為residence目前是nil 匠童。

如果你創(chuàng)建一個 Residence 實例,并為其 rooms 數(shù)組添加一些 Room 實例塑顺,然后將 Residence 實例賦值給john.residence 汤求,那就可以通過可選鏈和下標來訪問數(shù)組中的元素:
eg:

    let johnsHouse = Residence()
    johnsHouse.rooms.append(Room(name: "Living Room"))
    johnsHouse.rooms.append(Room(name: "Kitchen"))
    john.residence = johnsHouse
    if let firstRoomName = john.residence?[0].name {
        print("The first room name is \(firstRoomName).")
    } else {
        print("Unable to retrieve the first room name.")
    }
    // 打印 “The first room name is Living Room.”
    
    //訪問可選類型的下標
    var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
    testScores["Dave"]?[0] = 91
    testScores["Bev"]?[0] += 1
    testScores["Brian"]?[0] = 72//調(diào)用失敗
    // "Dave" 數(shù)組現(xiàn)在是 [91, 82, 84],"Bev" 數(shù)組現(xiàn)在是 [80, 94, 81]

6严拒、連接多層可選鏈式調(diào)用

? 通過可選鏈式調(diào)用訪問一個 Int 值首昔,將會返回 Int? ,無論使用了多少層可選鏈式調(diào)用糙俗。
? 類似的,通過可選鏈式調(diào)用訪問 Int? 值预鬓,依舊會返回 Int? 值巧骚,并不會返回 Int?? 。

    let johnsAddress = Address()
    johnsAddress.buildingName = "The Larches"
    johnsAddress.street = "Laurel Street"
    john.residence?.address = johnsAddress
    if let johnsStreet = john.residence?.address?.street {//多層可選
        print("John's street name is \(johnsStreet).")
    } else {
        print("Unable to retrieve the address.")
    }
    // 打印 “John's street name is Laurel Street.”

7格二、在方法的可選返回值上進行可選鏈式調(diào)用

在下面的例子中劈彪,通過可選鏈式調(diào)用來調(diào)用 Address 的 buildingIdentifier() 方法。這個方法返回 String? 類型 的值顶猜。如上所述沧奴,通過可選鏈式調(diào)用來調(diào)用該方法,最終的返回值依舊會是 String? 類型:

    if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
        print("John's building identifier is \(buildingIdentifier).")
    }
    // 打印 “John's building identifier is The Larches.”
    
    //如果要在該方法的返回值上進行可選鏈式調(diào)用长窄,在方法的圓括號后面加上問號即可:
    if let beginsWithThe = john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
        if beginsWithThe {
            print("John's building identifier begins with \"The\".")
        } else {
            print("John's building identifier does not begin with \"The\".")
        } }
    // 打印 “John's building identifier begins with "The".”

注意:
在上面的例子中滔吠,在方法的圓括號后面加上問號是因為你要在 buildingIdentifier() 方法的可選返回值上進行可選鏈式調(diào)用,而不是方法本身挠日。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疮绷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子嚣潜,更是在濱河造成了極大的恐慌冬骚,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件懂算,死亡現(xiàn)場離奇詭異只冻,居然都是意外死亡,警方通過查閱死者的電腦和手機计技,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進店門喜德,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人酸役,你說我怎么就攤上這事住诸〖莸ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵贱呐,是天一觀的道長丧诺。 經(jīng)常有香客問我,道長奄薇,這世上最難降的妖魔是什么驳阎? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮馁蒂,結(jié)果婚禮上呵晚,老公的妹妹穿的比我還像新娘。我一直安慰自己沫屡,他們只是感情好饵隙,可當我...
    茶點故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沮脖,像睡著了一般金矛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上勺届,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天驶俊,我揣著相機與錄音,去河邊找鬼免姿。 笑死饼酿,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的胚膊。 我是一名探鬼主播故俐,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼澜掩!你這毒婦竟也來了购披?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤肩榕,失蹤者是張志新(化名)和其女友劉穎刚陡,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體株汉,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡筐乳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了乔妈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蝙云。...
    茶點故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖路召,靈堂內(nèi)的尸體忽然破棺而出勃刨,到底是詐尸還是另有隱情波材,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布身隐,位于F島的核電站廷区,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏贾铝。R本人自食惡果不足惜隙轻,卻給世界環(huán)境...
    茶點故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望垢揩。 院中可真熱鬧玖绿,春花似錦、人聲如沸叁巨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锋勺。三九已至秤标,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宙刘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工牢酵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留悬包,地道東北人。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓馍乙,卻偏偏與公主長得像布近,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子丝格,可洞房花燭夜當晚...
    茶點故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容