// 可选链式调用
//“可选链式调用是一种可以在当前值可能为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()方法的可选返回值上进行可选链式调用,而不是方法本身。”