可空鏈?zhǔn)秸{(diào)用
可空鏈?zhǔn)秸{(diào)用是一種可以請(qǐng)求和調(diào)用屬性新娜、方法以及下標(biāo)的過程推穷,它的可空性體現(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)用失敗皆怕。
note:Swift的可空鏈?zhǔn)秸{(diào)用和Objective-c的消息為空有些想象毅舆,但是Swift可以使用在任意類型中,并且能夠檢查調(diào)用是否成功愈腾。
使用可空鏈?zhǔn)秸{(diào)用來強(qiáng)制展開
通過在想調(diào)用非空的屬性憋活、方法、或下標(biāo)的可空值后面放一個(gè)問號(hào)虱黄,可以定義一個(gè)可空鏈悦即。這一點(diǎn)很像在可空值后面放一個(gè)嘆號(hào)(!)來強(qiáng)制展開其值。它們的主要的區(qū)別在于當(dāng)可空值為空時(shí)可空鏈?zhǔn)街皇钦{(diào)用失敗橱乱,然而強(qiáng)制展開會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤辜梳。
為了反映可空鏈?zhǔn)秸{(diào)用可以在空對(duì)象nil上調(diào)用,不論這個(gè)調(diào)用的屬性泳叠、方法作瞄、下標(biāo)等返回的值是不是可空值,它的返回結(jié)果都是一個(gè)可空值危纫。我們可以利用這個(gè)返回值來判斷我們的可空鏈?zhǔn)秸{(diào)用是否成功宗挥,如果調(diào)用有返回值則說明調(diào)用成功节预,返回nil則說明調(diào)用失敗。
特別地属韧,可空鏈?zhǔn)秸{(diào)用的返回結(jié)果與原本的返回結(jié)果具有相同的類型,但是被包裝成了可空類型值蛤吓。當(dāng)可空鏈?zhǔn)秸{(diào)用成功時(shí)宵喂,一個(gè)本應(yīng)該返回Int的類型的結(jié)果將會(huì)返回Int?類型会傲。
class House {
var numberOfRooms = 1
}
class Person {
var house : House?
}
let john = Person()
//let num = john.house!.numberOfRooms
if let roomCount = john.house?.numberOfRooms {
print("John has \(roomCount) rooms")
}else{
print("John has no house")
}
為可空鏈?zhǔn)秸{(diào)用定義模型類
通過使用可空鏈?zhǔn)秸{(diào)用可以調(diào)用多層屬性锅棕,方法和下標(biāo)。這樣可以通過各種模型向下訪問各種子屬性淌山。并且判斷能否訪問子屬性裸燎,方法或下標(biāo)。
通過可空鏈?zhǔn)秸{(diào)用訪問屬性
可以通過可空鏈?zhǔn)秸{(diào)用訪問屬性的可空值泼疑,并且判斷訪問是否成功德绿。
通過可空鏈?zhǔn)秸{(diào)用來調(diào)用方法
可以通過可空鏈?zhǔn)秸{(diào)用來調(diào)用方法,并判斷是否調(diào)用成功退渗,即使這個(gè)方法沒有返回值移稳。如果在空值桑調(diào)用可空鏈?zhǔn)秸{(diào)用來調(diào)用這個(gè)方法,這個(gè)方法返回類型為Void会油?而不是Void个粱。
通過可空鏈?zhǔn)秸{(diào)用來訪問下標(biāo)
通過可空鏈?zhǔn)秸{(diào)用,我們可以用下標(biāo)來對(duì)可空值進(jìn)行讀取或?qū)懭敕妫⑶遗袛嘞聵?biāo)調(diào)用是否成功都许。
note:當(dāng)通過可空鏈?zhǔn)秸{(diào)用訪問可空值的下標(biāo)的時(shí)候,應(yīng)該將問號(hào)放在下標(biāo)放括號(hào)的前面而不是后面嫂冻〗赫鳎可空鏈?zhǔn)秸{(diào)用的問號(hào)一般直接跟在可控表達(dá)式的后面。
多層鏈接
可以通過多個(gè)個(gè)鏈接多個(gè)可空鏈?zhǔn)秸{(diào)用來向下訪問屬性絮吵,方法以及下標(biāo)弧烤。但是多層可空鏈?zhǔn)秸{(diào)用不會(huì)添加返回值的可空性。
也就是說:
- 如果我們訪問的值不是可空的蹬敲,通過可空鏈?zhǔn)秸{(diào)用將會(huì)返回可空值暇昂。
- 如果我們訪問的值已經(jīng)是可空的,通過可空鏈?zhǔn)秸{(diào)用不會(huì)變得“更”可空伴嗡。
因此:
- 通過可空鏈?zhǔn)秸{(diào)用訪問一個(gè)Int值急波,將會(huì)返回Int?瘪校,不過進(jìn)行了多次可空鏈?zhǔn)秸{(diào)用澄暮。
- 類似的名段,通過可空鏈?zhǔn)秸{(diào)用訪問Int?值泣懊,并不會(huì)變得更加可空伸辟。
對(duì)返回可空值的函數(shù)進(jìn)行鏈接
通過可空鏈?zhǔn)秸{(diào)用來獲取可空屬性值。我們還可以通過鏈?zhǔn)秸{(diào)用來調(diào)用返回可空值的方法馍刮,并且可以繼續(xù)對(duì)可空值進(jìn)行鏈接信夫。
</br></br></br>
錯(cuò)誤處理
錯(cuò)誤處理是響應(yīng)錯(cuò)誤以及從錯(cuò)誤中恢復(fù)的過程。Swift提供了在運(yùn)行時(shí)對(duì)可恢復(fù)錯(cuò)誤拋出卡啰,捕獲静稻,傳送和操作的高級(jí)支持。
某些操作并不能總是保證執(zhí)行所有代碼都可以執(zhí)行或總會(huì)產(chǎn)出有用的結(jié)果匈辱≌裢澹可選類型用來表示值可能為空,但是當(dāng)執(zhí)行失敗的時(shí)候亡脸,通常要去了解此次失敗是由何引起押搪,你的代碼就可以做出與之相應(yīng)的反應(yīng)。
note:Swift中的錯(cuò)誤處理涉及到錯(cuò)誤處理模式梗掰,這會(huì)用到cocoa和Objecive-c中的NSError嵌言。
表示并拋出錯(cuò)誤
在Swift中,錯(cuò)誤用遵循ErrorType協(xié)議類型的值來表示及穗。這個(gè)空協(xié)議表示一種可以用做錯(cuò)誤處理的類型摧茴。Swift的枚舉類型尤為適合塑造一組相關(guān)的錯(cuò)誤情形,枚舉的關(guān)聯(lián)值還可以提供額外的信息埂陆,表示某些錯(cuò)誤情形的性質(zhì)苛白。
enum VendingMachineError: ErrorType {
case InvalidSelection //選擇無效
case InsufficientFunds(coninsNeeded: Int) //金額不足
case OutOfStock // 缺貨
}
拋出一個(gè)錯(cuò)誤會(huì)讓我們對(duì)所發(fā)生的意外情況作出提示,表示正常的執(zhí)行流程不能被執(zhí)行下去焚虱。拋出錯(cuò)誤使用throw關(guān)鍵字购裙。
throw VedingMachineError.InsufficientFunds(coninsNeeded:5)
處理錯(cuò)誤
某個(gè)錯(cuò)誤被拋出時(shí),那個(gè)地方的某部分代碼必須要負(fù)責(zé)處理這個(gè)錯(cuò)誤(比如糾正這個(gè)錯(cuò)誤鹃栽、嘗試另外一種方式躏率、或時(shí)給用戶提示這個(gè)錯(cuò)誤)。Swift中有4種處理錯(cuò)誤的方式民鼓。我們可以把函數(shù)拋出的錯(cuò)誤傳遞給調(diào)用此函數(shù)代碼薇芝、用do-catch語句處理錯(cuò)誤、將錯(cuò)誤作為可選類型處理丰嘉、或者斷言此錯(cuò)誤根本不會(huì)發(fā)生夯到。
note:Swift中的錯(cuò)誤處理和其他語言中的try,catch和throw的異常處理很像饮亏。和其他語言(包括OC)的異常處理不同的是耍贾,Swift中的錯(cuò)誤處理并不涉及堆棧解退阅爽,這是一個(gè)計(jì)算昂貴的過程。就此而言荐开,throw語句的性能特性是可以和return語句相當(dāng)?shù)摹?/p>
1.用throwing函數(shù)傳遞錯(cuò)誤
用throws關(guān)鍵字來標(biāo)識(shí)一個(gè)可拋出錯(cuò)誤的函數(shù)付翁,方法或是構(gòu)造器。在函數(shù)聲明中的參數(shù)列表之后加上throws晃听。一個(gè)標(biāo)識(shí)了throws的函數(shù)被稱作throwing函數(shù)胆敞。如果這個(gè)函數(shù)還有返回類型,throws關(guān)鍵字需要寫在箭頭(->)的前面杂伟。
func canThrowErrors() throws -> String
func cannotThrowErrors() -> String
throwing函數(shù)從其內(nèi)部拋出錯(cuò)誤,并傳遞到函數(shù)被調(diào)用時(shí)所在的區(qū)域中仍翰。
note:只有throwing函數(shù)可以傳遞錯(cuò)誤赫粥。任何在某個(gè)非throwing函數(shù)內(nèi)部拋出的錯(cuò)誤只能在此函數(shù)內(nèi)部處理。
enum VendingMachineError: ErrorType {
case InvalidSelection //選擇無效
case InsufficientFunds(coninsNeeded: Int) //金額不足
case OutOfStock // 缺貨
}
struct Item {
var price : Int
var count : Int
}
class Vendingmachine {
var inventory = ["water": Item(price:12,count: 7),
"coco": Item(price: 10, count: 4),
"orange":Item(price: 7, count: 11)
]
var coinsDeposited = 0
func dispenseSnack(snack: String){
print("Dispensing \(snack)")
}
func vend(itemnamed name : String) throws {
guard var item = inventory[name] else{
throw VendingMachineError.InvalidSelection
}
guard item.count > 0 else{
throw VendingMachineError.OutOfStock
}
guard item.price <= coinsDeposited else{
throw VendingMachineError.InsufficientFunds(coninsNeeded: item.price - coinsDeposited)
}
coinsDeposited -= item.price
item.count -= 1
inventory[name] = item
dispenseSnack(name)
}
}
因?yàn)関end(itemNamed:)方法會(huì)傳遞出它拋出的任何錯(cuò)誤予借,在我們代碼中調(diào)用它的地方必須要直接處理這些錯(cuò)誤---使用do-catch語句越平,try?或try!灵迫,要么繼續(xù)將這些錯(cuò)誤傳遞下去秦叛。
func buyMostSnack(name:String,machine:Vendingmachine) throws {
try machine.vend(itemnamed: name)
}
因?yàn)関end(itemNamed:)方法能拋出錯(cuò)誤,所以在調(diào)用它的時(shí)候在它前面加了try關(guān)鍵字瀑粥。
2.用Do-Catch處理錯(cuò)誤
可以使用一個(gè)do-catch語句運(yùn)行一段閉包代碼來做錯(cuò)誤處理挣跋。如果在do語句中的代碼拋出了一個(gè)錯(cuò)誤,則這個(gè)錯(cuò)誤會(huì)與catch語句做匹配來決定哪條語句能處理它狞换。
do {
try expression
}catch pattern1{
}catch pattern2 where condition {
}
在catch后面寫一個(gè)模式(pattern)來標(biāo)示這個(gè)語句能處理什么樣的錯(cuò)誤避咆。如果一條catch語句沒帶一個(gè)模式,那么這條語句可以和任何錯(cuò)誤相匹配修噪,并且把錯(cuò)誤和一個(gè)名字為name的局部常量做綁定查库。
catch語句不必將do語句中代碼所拋出的每個(gè)可能的錯(cuò)誤都處理。如果沒有一條catch字句來處理錯(cuò)誤黄琼,錯(cuò)誤就會(huì)傳播到周圍的作用域樊销。然而錯(cuò)誤還是必須要被某個(gè)周圍餓作用域處理的---要么是一個(gè)外圍的do-catch錯(cuò)誤處理,要么是一個(gè)throwing函數(shù)的內(nèi)部脏款。
var machine = Vendingmachine()
machine.coinsDeposited = 8
do {
try buyMostSnack("coco", machine: machine)
}catch VendingMachineError.InvalidSelection{
print("InvalidSelection")
}catch VendingMachineError.OutOfStock {
print("OutOfStock")
}catch VendingMachineError.InsufficientFunds(let coinsNeeded){
print("InsufficientFunds please insert \(coinsNeeded)")
}
將錯(cuò)誤轉(zhuǎn)換成可選值
可以使用try围苫?通過將其轉(zhuǎn)換成一個(gè)可選值來處理錯(cuò)誤。如果在評(píng)估try弛矛?表達(dá)式時(shí)一個(gè)錯(cuò)誤被拋出够吩,哪么這個(gè)表達(dá)式的值就是nil。
func someThrowingFunction() throws -> Int {
//.....
}
let x = try? someThrowingFunction()
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
如果someThrowFunction()拋出一個(gè)錯(cuò)誤丈氓,x和y的值是nil周循。否則 x 和 y 的值就是該函數(shù)的返回值强法。注意無論someThrowFuction()的返回值類型是什么類型,x 和 y 都是這個(gè)類型的可選類型湾笛。
使錯(cuò)誤傳遞失敗
有時(shí)候我知道某個(gè)throwing函數(shù)實(shí)際上在運(yùn)行時(shí)是不會(huì)拋出錯(cuò)誤的饮怯。在這種條件下,我們可以在表達(dá)式前面寫try嚎研!來使錯(cuò)誤傳遞失敗蓖墅,并把調(diào)用包裝在一個(gè)運(yùn)行時(shí)斷言中來判定會(huì)不會(huì)有錯(cuò)誤拋出。如果實(shí)際上確實(shí)拋出了錯(cuò)誤临扮,我們就會(huì)得到一個(gè)運(yùn)行時(shí)錯(cuò)誤论矾。
let photo = try! loadImage("./Resources/John.png")
指定清理操作
可以使用defer語句在代碼執(zhí)行到要離開當(dāng)前的代碼段之前去執(zhí)行一套語句。該語句讓我們能夠作一些應(yīng)該被執(zhí)行的必要清理工作杆勇,不管是以何種方式離開當(dāng)前的代碼段的 ---無論時(shí)由于拋出錯(cuò)誤而離開贪壳,或是因?yàn)橐粭lreturn或者break的類似語句。defer語句將代碼的執(zhí)行延遲到當(dāng)前的作用域退出之前蚜退。該語句由defer關(guān)鍵字和要被延時(shí)執(zhí)行的語句組成闰靴。延時(shí)執(zhí)行的語句不會(huì)包含任何會(huì)將控制權(quán)移交語句外面的代碼,例如一條break或是return語句钻注,或是拋出一個(gè)錯(cuò)誤蚂且。延遲執(zhí)行的操作是按照它們被指定的相反順序執(zhí)行---意思是第一條defer語句中的代碼執(zhí)行是在第二條defer語句中代碼被執(zhí)行之后,以此類推
func test(){
print("test begin")
defer{
print("1")
}
defer{
print("2")
}
defer{
print("3")
}
defer{
print("4")
}
print("test end")
}
test()
類型轉(zhuǎn)換
類型轉(zhuǎn)換可以判斷實(shí)例的類型幅恋,也可以將實(shí)例看做是其父類或子類的實(shí)例杏死。
類型轉(zhuǎn)換在Swift中使用 is 和 as 操作符實(shí)現(xiàn)。這兩個(gè)操作提供了一種簡單達(dá)意的方式去檢查值的類型或者轉(zhuǎn)換它的類型捆交。
我們也可以用它來檢查一個(gè)類是否實(shí)現(xiàn)了某個(gè)協(xié)議
定義一個(gè)類層次作為例子
我們可以將類型轉(zhuǎn)換用在類和子類的層次結(jié)構(gòu)上识埋,檢查特定類實(shí)例的類型并且轉(zhuǎn)換這個(gè)類實(shí)例的類型成為這個(gè)層次結(jié)構(gòu)中其他類型。
class MediaItem {
var name:String
init(name:String){
self.name = name
}
}
class Movie:MediaItem{
var director: String
init(name:String, director:String){
self.director = director;
super.init(name: name)
}
}
class Song: MediaItem {
var artist : String
init(name:String, artist: String){
self.artist = artist
super.init(name: name)
}
}
let arr = [
Movie(name: "one", director: "1"),
Song(name: "one", artist: "1"),
Movie(name: "two", director: "2"),
Movie(name: "three", director: "3"),
Song(name: "three", artist: "3")
]
檢查類型
用類型檢查操作符(is)來檢查一個(gè)實(shí)例是否屬于特定子類型零渐。若實(shí)例屬于那個(gè)子類型窒舟,類型檢查操作符返回true , 否則返回false 诵盼。
for item in arr {
if item is Movie {
print("\(item) is movie")
}
if item is Song {
print("\(item) is song")
}
//總是true
if item is MediaItem {
print("\(item) is MediaItem")
}
}
向下轉(zhuǎn)型
某類型的一個(gè)常量或變量可能在幕后實(shí)際上屬于一個(gè)子類惠豺。當(dāng)確定這種情況下,我們可以嘗試向下轉(zhuǎn)到它的子類型风宁,用類型轉(zhuǎn)換符(as洁墙?或 as!)
因?yàn)橄蛳罗D(zhuǎn)型可能會(huì)失敗,類型轉(zhuǎn)型操作符帶有兩種不同形式戒财。
- 條件形式 as热监?返回一個(gè)你試圖向下轉(zhuǎn)成的類型的可選值。
- 強(qiáng)制式 as饮寞!把試圖向下轉(zhuǎn)型和強(qiáng)制解包結(jié)果作為一個(gè)混合動(dòng)作孝扛。
只有當(dāng)可以確定向下轉(zhuǎn)型一定會(huì)成功時(shí)列吼,才使用強(qiáng)制形式(as!)苦始。當(dāng)試圖向下轉(zhuǎn)型為一個(gè)不正確的類型時(shí)寞钥,強(qiáng)制形式的類型轉(zhuǎn)換會(huì)觸發(fā)一個(gè)運(yùn)行時(shí)錯(cuò)誤。
note:轉(zhuǎn)換沒有真的改變實(shí)例或它的值陌选。潛在的根本的實(shí)例保持不變理郑;只是簡單地把它作為它被轉(zhuǎn)成的類來使用。
for item in arr {
if let movie = item as? Movie{
print("movie:\(movie.name) and \(movie.director)")
}else if let song = item as? Song {
print("song:\(song.name) and \(song.artist)")
}
}
Any和AnyObject的類型轉(zhuǎn)換
Swift為不確定類型提供了兩種特殊類型別名:
- AnyObject可以代表任何class類型的實(shí)例咨油。
- Any可以表示任何類型您炉,包括方法類型(function types)
note:只有當(dāng)我們明確的需要它的行為和功能時(shí)才使用Any和AnyObject。在我們的代碼里使用我們期望的明確的類型總是更好的役电。
AnyObject類型
當(dāng)在工作中使用Cocoa APIs邻吭,我們一般會(huì)接收一個(gè)[AnyObject]類型的數(shù)組,或者說“一個(gè)任何對(duì)象類型的數(shù)組”宴霸。這是因?yàn)镺bjective-c沒有明確的類型化數(shù)組。但是膏蚓,我們常称靶唬可以從API提供的信息中清晰地確定數(shù)組中對(duì)象的類型 。
在這些情況下驮瞧,我們可以使用強(qiáng)制形式類型轉(zhuǎn)換(as)來下轉(zhuǎn)在數(shù)組中的每一項(xiàng)到比AnyObject更明確的類型氓扛,不需要可選解析。
let someObjects: [AnyObject] = [
Movie(name: "one", director: "1"),
Movie(name: "two", director: "2"),
Movie(name: "three", director: "3"),
Movie(name: "four", director: "4"),
Movie(name: "five", director: "5")
]
for object in someObjects {
let movie = object as! Movie
print("Movie:\(movie.name) and \(movie.director)")
}
為了變成一個(gè)更短的形式论笔,下面轉(zhuǎn)someObjects數(shù)組為[Movie]類型來替代下轉(zhuǎn)數(shù)組中的每一項(xiàng)的方式采郎。
for movie in someObjects as! [Movie] {
print("Movie:\(movie.name) and \(movie.director)")
}
Any類型
這里有個(gè)例子,使用Any類型來和混合的不同類型一起工作狂魔。包括方法類型和非class類型蒜埋。它們創(chuàng)建了一個(gè)可以存儲(chǔ)Any類型的數(shù)組things。
var things = [Any]()
things.append(0)
things.append(0.0)
things.append(42)
things.append(3.1415926)
things.append("hello")
things.append({(name:String) -> String in "hello \(name)"})
嵌套類型
枚舉類型常被用于特定類或結(jié)構(gòu)體的功能最楷。也能夠在多種變量類型的環(huán)境中整份,方便的定義通用類或結(jié)構(gòu)體來使用,為了實(shí)現(xiàn)這種功能籽孙,Swift允許你定義嵌套類型烈评,可以在枚舉類型、類和結(jié)構(gòu)體種定義支持嵌套的類型犯建。
要在一個(gè)類型中嵌套另一個(gè)類型讲冠,將需要嵌套的類型的定義寫在被嵌套類型的區(qū)域{}內(nèi),而且可以根據(jù)需要定義多級(jí)嵌套适瓦。
嵌套類型實(shí)例
在外部對(duì)嵌套類型的引用竿开,以被嵌套類型的名字為前綴谱仪,加上所要引用的屬性,方法等德迹。
struct Student {
struct PersonInfo {
var name:String
var age = 0
var sex = 0
}
struct School {
var name:String
var address:String
}
var info:PersonInfo
var school:School
}
let stu = Student(info: Student.PersonInfo(name:"hello",age:10,sex:1), school: Student.School(name:"school",address:"address")
stu.school.hello()
stu.school.address