Optional Chain(可选链式调用)

// 可选链式调用
//“可选链式调用是一种可以在当前值可能为nil的可选值上请求和调用属性、方法及下标的方法。如果可选值有值,那么调用就会成功;如果可选值是nil,那么调用将返回nil。多个调用可以连接在一起形成一个调用链,如果其中任何一个节点为nil,整个调用链都会失败,即返回nil。”
//“Swift 的可选链式调用和 Objective-C 中向nil发送消息有些相像,但是 Swift 的可选链式调用可以应用于任意类型,并且能检查调用是否成功”

//1.使用可选链式调用代替强制展开
//“通过在想调用的属性、方法、或下标的可选值后面放一个问号(?),可以定义一个可选链。这一点很像在可选值后面放一个叹号(!)来强制展开它的值。它们的主要区别在于当可选值为空时可选链式调用只会调用失败,然而强制展开将会触发运行时错误。”
//“为了反映可选链式调用可以在空值(nil)上调用的事实,不论这个调用的属性、方法及下标返回的值是不是可选值,它的返回结果都是一个可选值。你可以利用这个返回值来判断你的可选链式调用是否调用成功,如果调用有返回值则说明调用成功,返回nil则说明调用失败。”
//“特别地,可选链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了一个可选值。例如,使用可选链式调用访问属性,当可选链式调用成功时,如果属性原本的返回结果是Int类型,则会变为Int?类型。”

class Person {
    var residence: Residence?
}
class Residence{
    var numberOfRooms = 1
}
let john = Person()
//let roomCount = john.residence!.numberOfRooms
//强制展开值为nil的可选值会报运行时错误
if let roomCount = john.residence?.numberOfRooms {
    print("john's residence has \(roomCount) rooms")
}else{
    print("unable to retrieve the number of rooms")
}

//“在residence后面添加问号之后,Swift 就会在residence不为nil的情况下访问numberOfRooms”
//“因为访问numberOfRooms有可能失败,可选链式调用会返回Int?类型,或称为“可选的 Int”。如上例所示,当residence为nil的时候,可选的Int将会为nil,表明无法访问numberOfRooms。访问成功时,可选的Int值会通过可选绑定展开,并赋值给非可选类型的roomCount常量。要注意的是,即使numberOfRooms是非可选的Int时,这一点也成立。只要使用可选链式调用就意味着numberOfRooms会返回一个Int?而不是Int。”

//2. 为可选链式调用定义模型类
//“通过使用可选链式调用可以调用多层属性、方法和下标。这样可以在复杂的模型中向下访问各种子属性,并且判断能否访问子属性的属性、方法或下标。”

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
        }
    }
}
class TestResidence {
    var rooms = [Room]()
    var numberOfRoomsd:Int{
        return rooms.count
    }
    subscript(i:Int)->Room{
        get{
            return rooms[i]
        }
        set{
            rooms[i] = newValue
        }
    }
    func printNumberOfRooms(){
        print("the number of rooms is \(numberOfRoomsd)")
    }
    var address:Address?
}
class TestPerson {
    var testResidence : TestResidence?
}

//3. 通过可选链式调用访问属性

let johntatol = TestPerson()
if let roomCount = johntatol.testResidence?.numberOfRoomsd{
    print("johntatol's testResidence has \(roomCount) rooms")
}else{
    print("unable to retrieve the number of rooms")
}

//可以通过可选链式调用来设置属性值

let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
johntatol.testResidence?.address = someAddress

//“面代码中的赋值过程是可选链式调用的一部分,这意味着可选链式调用失败时,等号右侧的代码不会被执行。对于上面的代码来说,很难验证这一点,因为像这样赋值一个常量没有任何副作用”

func creatAdress()->Address{
    print("Function was called")
    let someAdress = Address()
    someAdress.buildingNumber = "29"
    someAdress.street = "Acacia Road"
    return someAdress
}

johntatol.testResidence?.address = creatAdress()

// 没有打印,function was called。说明creatAdress() 没有被执行。因为testResidence是nil的。

//4. 通过可选链式调用调用方法
//“可以通过可选链式调用来调用方法,并判断是否调用成功,即使这个方法没有返回值”
//“没有返回值的方法具有隐式的返回类型Void,如无返回值函数中所述。这意味着没有返回值的方法也会返回(),或者说空的元组“如果在可选值上通过可选链式调用来调用这个方法,该方法的返回类型会是Void?,而不是Void,因为通过可选链式调用得到的返回值都是可选的。这样我们就可以使用if语句来判断能否成功调用printNumberOfRooms()方法,即使方法本身没有定义返回值。通过判断返回值是否为nil可以判断调用是否成功”

if johntatol.testResidence?.printNumberOfRooms() != nil {
    print("it was possible to print the rooms")
}else{
    print("it was not possible to print number of rooms")
}
//打印it was not possible to print number of rooms

//同样,“可以据此判断通过可选链式调用为属性赋值是否成功”
if (johntatol.testResidence?.address = someAddress) != nil {
    print("可以赋值属性")
}else{
    print("不能赋值属性")
}
//打印不能赋值属性

// 5. 可选链式调用访问下标
//“通过可选链式调用,我们可以在一个可选值上访问下标,并且判断下标调用是否成功”
//“注意 通过可选链式调用访问可选值的下标时,应该将问号放在下标方括号的前面而不是后面。可选链式调用的问号一般直接跟在可选表达式的后面”

if let firstRoomName = johntatol.testResidence?[0].name {
    print("\(firstRoomName) 存在")
}else{
    print("不能获得第一个房间的名字")
}
//打印 不能获得第一个房间的名字
johntatol.testResidence?[0] = Room(name:"bathroom")
//赋值同样会失败,因为testResidence目前是nil
let johnsHouse = TestResidence()
johnsHouse.rooms.append(Room(name:"livingroom"))
johntatol.testResidence = johnsHouse
//可选值赋值不用使用? 可选值的属性方法调用赋值需要用?
if let anotherRoomName = johntatol.testResidence?[0].name {
    print("赋值成功了,\(anotherRoomName)")
}else{
    print("赋值失败")
}
//打印 赋值成功了,livingroom

//“如果下标返回可选类型值,比如 Swift 中Dictionary类型的键的下标,可以在下标的结尾括号后面放一个问号来在其可选返回值上进行可选链式调用:

var testScores = ["dave":[98,90,80],"bev":[70,49,82]]
testScores["dave"]?[0] = 91
testScores["bev"]?[0] += 1
testScores["brian"]?[0] = 75
print("\(testScores)")
//打印 ["bev": [71, 49, 82], "dave": [91, 90, 80]]

//6. 连续多层的可选链式调用
//“可以通过连接多个可选链式调用在更深的模型层级中访问属性、方法以及下标。然而,多层可选链式调用不会增加返回值的可选层级”
/*
“如果你访问的值不是可选的,可选链式调用将会返回可选值。
如果你访问的值就是可选的,可选链式调用不会让可选返回值变得“更可选”。

因此:
通过可选链式调用访问一个Int值,将会返回Int?,无论使用了多少层可选链式调用。
类似的,通过可选链式调用访问Int?值,依旧会返回Int?值,并不会返回Int??。”

*/

if let jhonStress = johntatol.testResidence?.address?.street {
    print("jhon's stress name is \(jhonStress)")
}else{
    print("unable to retrieve the address")
}
let jhonAddress = Address()
jhonAddress.buildingName = "the larches"
jhonAddress.street = "laurel stress"
johntatol.testResidence?.address = jhonAddress
if let jhonstress = johntatol.testResidence?.address?.street {
    print("the stress is \(jhonstress)")
}else{
   print("can't to retrieve")
}
//打印 the stress is laurel stress  赋值成功

//7. “在方法的可选返回值上进行可选链式调用”
//“我们还可以在一个可选值上通过可选链式调用来调用方法,并且可以根据需要继续在方法的可选返回值上进行可选链式调用”

if let buildingIdentifier = johntatol.testResidence?.address?.buildingIdentifier() {
    print("johntatol's builing identifier is \(buildingIdentifier)")
}
//打印johntatol's builing identifier is the larches

//“如果要在该方法的返回值上进行可选链式调用,在方法的圆括号后面加上问号即可”

if let beginsWithThe = johntatol.testResidence?.address?.buildingIdentifier()?.hasPrefix("The"){
    if beginsWithThe {
        print("jhon's building identifier begins with \"The\"...")
    }else{
        print("jhon's building identifier does not beging with \"The\"...")
    }
}else{
    print("can't reach ,the optional return nil")
}
//打印jhon's building identifier does not beging with "The"...

//“在方法的圆括号后面加上问号是因为你要在buildingIdentifier()方法的可选返回值上进行可选链式调用,而不是方法本身。”

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容