可選類型
在swift程序中我們會(huì)處理各種各樣的錯(cuò)誤,比如說(shuō)解析一個(gè)Dictionary:
let dic = ["key": "value"]
let value = dic["key"]
value
實(shí)際上是一個(gè)optional類型
都许,也就是說(shuō)value可能為String類型也可能是nil缆娃,處理這種情況我們一般對(duì)value
嘗試解包,如果成功就取里面的值:
if let value = dic["key"] {
print(value)
}
使用可選類型
進(jìn)行錯(cuò)誤處理是一種方式段标,但是使用這種錯(cuò)誤處理方式有一定的局限性拐辽。比如說(shuō)我們想連接網(wǎng)絡(luò),如果沒(méi)有連接上就返回一個(gè)nil佛吓,雖然可以但是我們只知道網(wǎng)絡(luò)沒(méi)連接上宵晚,但是卻不知道沒(méi)連接上的原因到底是服務(wù)端超時(shí)呢,還是客戶端出現(xiàn)了問(wèn)題辈毯,所以使用一套完善的錯(cuò)誤處理機(jī)制能幫助我們更好的管理錯(cuò)誤坝疼。
表示并拋出錯(cuò)誤
舉一個(gè)例子,假如我們需要讀取磁盤上的某個(gè)文件進(jìn)行處理谆沃,這個(gè)任務(wù)可能會(huì)有多種失敗的情況钝凶,包括指定路徑下文件并不存在,指定文件類型不正確等唁影。我們的代碼看起來(lái)是這樣的:
class FileManager {
struct File: CustomStringConvertible {
let name: String
let type: String
let isReadable: Bool
let size: Int
var description: String {
return "\(name).\(type) size is : \(size) Bytes"
}
}
private var files = ["path1": File(name: "file1", type: "text", isReadable: true, size: 3096),
"path2": File(name: "file2", type: "mp3", isReadable: false, size: 10240),
"path3": File(name: "file3", type: "flv", isReadable: true, size: 20480)
]
func readFile(path: String, type: String) -> File? {
// 檢查文件是否存在
guard let file = files[path] else {
return nil
}
// 檢查文件是否有可讀權(quán)限
guard file.isReadable == true else {
return nil
}
// 檢查文件類型
guard file.type == type else {
return nil
}
return file
}
}
我們定義了一個(gè)FileManager
類耕陷,它有一個(gè)readFile(path: String, type: String) -> File?
方法,該方法傳遞一個(gè)路徑和文件類型据沈,返回一個(gè)可選類型哟沫,如果文件不存在或者沒(méi)有可讀權(quán)限或者類型不匹配都會(huì)返回nil
,但是這并不是我們的想要的結(jié)果锌介,我們想要的是讀取文件失敗的原因
嗜诀。
在swift中,錯(cuò)誤用符合ErrorType
協(xié)議的的類型的值來(lái)表示孔祸。代表一個(gè)可以拋出錯(cuò)誤的類型隆敢。
-
我們可以使用枚舉來(lái)定義錯(cuò)誤類型:
enum ReadFileError: ErrorType { case FileNotExists // 文件不存在 case FileIsReadable // 沒(méi)有可讀權(quán)限 case TypeMismatch // 類型不匹配 }
-
然后我們修改
readFile()
方法func readFile(path: String, type: String) throws -> File? { // 檢查文件是否存在 guard let file = files[path] else { throw ReadFileError.FileNotExists } // 檢查文件是否有可讀權(quán)限 guard file.isReadable == true else { throw ReadFileError.FileNotReadable } // 檢查文件類型 guard file.type == type else { throw ReadFileError.TypeMismatch } return file }
注意需要在返回類型
->
前面加上throws
關(guān)鍵字,代表這是一個(gè)可能拋出異常的方法崔慧。然后在
guard
語(yǔ)句的else
條件里面拋出錯(cuò)誤拂蝎,這樣我們就能知道讀取文件失敗是什么原因?qū)е碌牧恕?/p>
處理錯(cuò)誤
readFile(path: String, type: String)
方法會(huì)傳遞出它拋出的所有錯(cuò)誤,所有你要么使用do-catch
語(yǔ)句惶室,要么將錯(cuò)誤繼續(xù)傳遞下去温自。
- throwing函數(shù)傳遞錯(cuò)誤
比如我們想讀取一個(gè)text類型的文件,我們的代碼可能是這樣的:
func readTextFile(path: String) throws -> File? {
return try readFile(path, type: "text")
}
然后我們嘗試讀取一個(gè).text
的文件:
let fileManager = FileManager()
if let file = try fileManager.readTextFile("path1") {
print(file.description)
}
打印出:
file1.text size is : 3096 Bytes
- 使用
do-catch
進(jìn)行錯(cuò)誤處理
- 使用
do {
try fileManager.readFile("noSuchFilePath", type: "text")
} catch {
print(error)
}
打印出FileNotExists
文件不存在的錯(cuò)誤皇钞。
如果我們想對(duì)具體的錯(cuò)誤進(jìn)行處理悼泌,可以這樣:
do {
try fileManager.readFile("noSuchFilePath", type: "text")
} catch ReadFileError.FileNotExists {
print("File Not Exists")
} catch ReadFileError.FileNotReadable {
print("File Not Readable")
} catch ReadFileError.TypeMismatch {
print("Type Not Matched")
} catch {
print("unkown error")
}
別忘了在處理了所有錯(cuò)誤之后在添加一個(gè)catch
,因?yàn)橛锌赡軙?huì)有其他異常出現(xiàn)鹅士。
如果我們的錯(cuò)誤很多券躁,難免會(huì)寫很多catch
語(yǔ)句,可以這樣處理:
do {
try fileManager.readFile("noSuchFilePath", type: "text")
} catch let error as ReadFileError {
// 使用 switch 進(jìn)行處理錯(cuò)誤
} catch {
print("Unkown Error")
}
使用as
關(guān)鍵字將ErrorType
類型轉(zhuǎn)換成ReadFileError
類型掉盅。
defer語(yǔ)句
defer
表示即將在離開(kāi)當(dāng)前代碼塊時(shí)執(zhí)行操作也拜。該語(yǔ)句讓你能執(zhí)行一些必要的清理工作,不管是以何種方式離開(kāi)當(dāng)前代碼塊的——無(wú)論是由于拋出錯(cuò)誤而離開(kāi)趾痘,還是由于諸如return
或者break
的語(yǔ)句慢哈。例如,你可以用defer
語(yǔ)句來(lái)確保文件描述符得以關(guān)閉永票,以及手動(dòng)分配的內(nèi)存得以釋放卵贱。
func processFile(file: File) {
file.open()
defer {
file.close()
}
// 可能拋出錯(cuò)誤的代碼
// ...
// ...
}
比如上面(偽代碼)我們讀取了一個(gè)文件,然后需要對(duì)該文件進(jìn)行調(diào)用open()
侣集,處理完之后我們需要關(guān)閉close()
键俱,但是中間可能會(huì)拋出錯(cuò)誤,導(dǎo)致我們的文件打開(kāi)了沒(méi)有關(guān)閉世分,使用defer
就能確保close()
操作會(huì)被執(zhí)行编振。代碼塊里面可以使用多個(gè)defer
。
實(shí)際上defer
是控制轉(zhuǎn)移類關(guān)鍵字臭埋,它被當(dāng)做其他語(yǔ)言(例如java
)的finally
關(guān)鍵字來(lái)使用踪央。
值得一提的是,當(dāng)代碼塊里面有過(guò)多的控制轉(zhuǎn)移這類的語(yǔ)句時(shí)瓢阴,反而會(huì)導(dǎo)致程序非常的亂畅蹂、可讀性變差,所以一定要適當(dāng)?shù)氖褂谩?/p>
以此紀(jì)錄swift
學(xué)習(xí)之旅荣恐。