錯誤處理
錯誤處理(Error handling)?是響應(yīng)錯誤以及從錯誤中恢復(fù)的過程。Swift 在運行時提供了拋出、捕獲、傳遞和操作可恢復(fù)錯誤(recoverable errors)的一等支持(first-class support)。
某些操作無法保證總是執(zhí)行完所有代碼或生成有用的結(jié)果??蛇x類型用來表示值缺失,但是當(dāng)某個操作失敗時,理解造成失敗的原因有助于你的代碼作出相應(yīng)的應(yīng)對。
在 Swift 中,錯誤用遵循?Error?協(xié)議的類型的值來表示。這個空協(xié)議表明該類型可以用于錯誤處理。
Swift 的枚舉類型尤為適合構(gòu)建一組相關(guān)的錯誤狀態(tài),枚舉的關(guān)聯(lián)值還可以提供錯誤狀態(tài)的額外信息。
拋出一個錯誤可以讓你表明有意外情況發(fā)生,導(dǎo)致正常的執(zhí)行流程無法繼續(xù)執(zhí)行。拋出錯誤使用?throw語句。
處理錯誤
某個錯誤被拋出時,附近的某部分代碼必須負責(zé)處理這個錯誤,例如糾正這個問題、嘗試另外一種方式、或是向用戶報告錯誤。
Swift 中有?4?種處理錯誤的方式。你可以把函數(shù)拋出的錯誤傳遞給調(diào)用此函數(shù)的代碼、用?do-catch?語句處理錯誤、將錯誤作為可選類型處理、或者斷言此錯誤根本不會發(fā)生。每種方式在下面的小節(jié)中都有描述。
為了標(biāo)識出這些地方,在調(diào)用一個能拋出錯誤的函數(shù)、方法或者構(gòu)造器之前,加上?try?關(guān)鍵字,或者?try??或?try!?這種變體。
注意:Swift 中的錯誤處理并不涉及解除調(diào)用棧,這是一個計算代價高昂的過程。就此而言,throw?語句的性能特性是可以和?return?語句相媲美的。
用 throwing 函數(shù)傳遞錯誤 :為了表示一個函數(shù)、方法或構(gòu)造器可以拋出錯誤,在函數(shù)聲明的參數(shù)之后加上?throws?關(guān)鍵字。一個標(biāo)有?throws?關(guān)鍵字的函數(shù)被稱作?throwing 函數(shù)。如果這個函數(shù)指明了返回值類型,throws?關(guān)鍵詞需要寫在返回箭頭(->)的前面。
一個 throwing 函數(shù)可以在其內(nèi)部拋出錯誤,并將錯誤傳遞到函數(shù)被調(diào)用時的作用域。
注意:只有 throwing 函數(shù)可以傳遞錯誤。任何在某個非 throwing 函數(shù)內(nèi)部拋出的錯誤只能在函數(shù)內(nèi)部處理。
?throw?語句會立即退出方法
用 Do-Catch 處理錯誤 :你可以使用一個?do-catch?語句運行一段閉包代碼來處理錯誤。如果在?do?子句中的代碼拋出了一個錯誤,這個錯誤會與?catch?子句做匹配,從而決定哪條子句能處理它。在?catch?后面寫一個匹配模式來表明這個子句能處理什么樣的錯誤。如果一條?catch?子句沒有指定匹配模式,那么這條子句可以匹配任何錯誤,并且把錯誤綁定到一個名字為?error?的局部常量。
將錯誤轉(zhuǎn)換成可選值:可以使用?try??通過將錯誤轉(zhuǎn)換成一個可選值來處理錯誤。如果是在計算?try??表達式時拋出錯誤,該表達式的結(jié)果就為?nil。
禁用錯誤傳遞 :有時你知道某個?throwing?函數(shù)實際上在運行時是不會拋出錯誤的,在這種情況下,你可以在表達式前面寫?try!?來禁用錯誤傳遞,這會把調(diào)用包裝在一個不會有錯誤拋出的運行時斷言中。如果真的拋出了錯誤,你會得到一個運行時錯誤。
指定清理操作 :你可以使用?defer?語句在即將離開當(dāng)前代碼塊時執(zhí)行一系列語句。該語句讓你能執(zhí)行一些必要的清理工作,不管是以何種方式離開當(dāng)前代碼塊的——無論是由于拋出錯誤而離開,或是由于諸如?return、break?的語句。例如,你可以用?defer?語句來確保文件描述符得以關(guān)閉,以及手動分配的內(nèi)存得以釋放。
defer?語句將代碼的執(zhí)行延遲到當(dāng)前的作用域退出之前。該語句由?defer?關(guān)鍵字和要被延遲執(zhí)行的語句組成。延遲執(zhí)行的語句不能包含任何控制轉(zhuǎn)移語句,例如?break、return?語句,或是拋出一個錯誤。延遲執(zhí)行的操作會按照它們聲明的順序從后往前執(zhí)行——也就是說,第一條?defer?語句中的代碼最后才執(zhí)行,第二條?defer?語句中的代碼倒數(shù)第二個執(zhí)行,以此類推。最后一條語句會第一個執(zhí)行。
類型轉(zhuǎn)換:類型轉(zhuǎn)換可以判斷實例的類型,也可以將實例看做是其父類或者子類的實例。
類型轉(zhuǎn)換在 Swift 中使用?is?和?as?操作符實現(xiàn)。
檢查類型 :用類型檢查操作符(is)來檢查一個實例是否屬于特定子類型。若實例屬于那個子類型,類型檢查操作符返回?true,否則返回?false。
向下轉(zhuǎn)型 :某類型的一個常量或變量可能在幕后實際上屬于一個子類。當(dāng)確定是這種情況時,你可以嘗試用類型轉(zhuǎn)換操作符(as??或?as!)向下轉(zhuǎn)到它的子類型。
因為向下轉(zhuǎn)型可能會失敗,類型轉(zhuǎn)型操作符帶有兩種不同形式。條件形式?as??返回一個你試圖向下轉(zhuǎn)成的類型的可選值。強制形式?as!?把試圖向下轉(zhuǎn)型和強制解包轉(zhuǎn)換結(jié)果結(jié)合為一個操作。
Any?和?AnyObject?的類型轉(zhuǎn)換?
Swift 為不確定類型提供了兩種特殊的類型別名:
Any?可以表示任何類型,包括函數(shù)類型。
AnyObject?可以表示任何類類型的實例。
只有當(dāng)你確實需要它們的行為和功能時才使用?Any?和?AnyObject。最好還是在代碼中指明需要使用的類型。
Any?類型可以表示所有類型的值,包括可選類型。Swift 會在你用?Any?類型來表示一個可選值的時候,給你一個警告。如果你確實想使用?Any?類型來承載可選值,你可以使用?as?操作符顯式轉(zhuǎn)換為?Any,如下所示:
let opNum: Int? = 3
things.append(opNum) ?//警告
things.append(opNum as Any) ?//沒有警告
嵌套類型
擴展:擴展可以給一個現(xiàn)有的類,結(jié)構(gòu)體,枚舉,還有協(xié)議添加新的功能。它還擁有不需要訪問被擴展類型源代碼就能完成擴展的能力(即逆向建模)。擴展和 Objective-C 的分類很相似。(與 Objective-C 分類不同的是,Swift 擴展是沒有名字的。)
Swift 中的擴展可以:
添加計算型實例屬性和計算型類屬性
定義實例方法和類方法
提供新的構(gòu)造器
定義下標(biāo)
定義和使用新的嵌套類型
使已經(jīng)存在的類型遵循(conform)一個協(xié)議
可變實例方法 :通過擴展添加的實例方法同樣也可以修改(或?mutating(改變))實例本身。結(jié)構(gòu)體和枚舉的方法,若是可以修改?self?或者它自己的屬性,則必須將這個實例方法標(biāo)記為?mutating,就像是改變了方法的原始實現(xiàn)。
協(xié)議:協(xié)議?定義了一個藍圖,規(guī)定了用來實現(xiàn)某一特定任務(wù)或者功能的方法、屬性,以及其他需要的東西。類、結(jié)構(gòu)體或枚舉都可以遵循協(xié)議,并為協(xié)議定義的這些要求提供具體實現(xiàn)。某個類型能夠滿足某個協(xié)議的要求,就可以說該類型遵循這個協(xié)議。除了遵循協(xié)議的類型必須實現(xiàn)的要求外,還可以對協(xié)議進行擴展,通過擴展來實現(xiàn)一部分要求或者實現(xiàn)一些附加功能,這樣遵循協(xié)議的類型就能夠使用這些功能。
屬性要求 :協(xié)議可以要求遵循協(xié)議的類型提供特定名稱和類型的實例屬性或類型屬性。協(xié)議不指定屬性是存儲屬性還是計算屬性,它只指定屬性的名稱和類型。此外,協(xié)議還指定屬性是可讀的還是可讀可寫的。
如果協(xié)議要求屬性是可讀可寫的,那么該屬性不能是常量屬性或只讀的計算型屬性。如果協(xié)議只要求屬性是可讀的,那么該屬性不僅可以是可讀的,如果代碼需要的話,還可以是可寫的。
協(xié)議總是用?var?關(guān)鍵字來聲明變量屬性,在類型聲明后加上?{ set get }?來表示屬性是可讀可寫的,可讀屬性則用?{ get }?來表示:
在協(xié)議中定義類型屬性時,總是使用?static?關(guān)鍵字作為前綴。當(dāng)類類型遵循協(xié)議時,除了?static?關(guān)鍵字,還可以使用?class?關(guān)鍵字來聲明類型屬性:
協(xié)議作為類型 :盡管協(xié)議本身并未實現(xiàn)任何功能,但是協(xié)議可以被當(dāng)做一個功能完備的類型來使用。協(xié)議作為類型使用,有時被稱作「存在類型」,這個名詞來自「存在著一個類型 T,該類型遵循協(xié)議 T」。
協(xié)議可以像其他普通類型一樣使用,使用場景如下:
作為函數(shù)、方法或構(gòu)造器中的參數(shù)類型或返回值類型
作為常量、變量或?qū)傩缘念愋?/p>
作為數(shù)組、字典或其他容器中的元素類型
通過擴展遵循并采納協(xié)議,和在原始定義中遵循并符合協(xié)議的效果完全相同
協(xié)議和可選要求都必須帶上?@objc?屬性。標(biāo)記?@objc?特性的協(xié)議只能被繼承自 Objective-C 類的類或者?@objc?類遵循,其他類以及結(jié)構(gòu)體和枚舉均不能遵循這種協(xié)議。