在Swift 2.0版本中,Swift語言對其錯誤處理進行了新的設計,固然了,從新設計後的結果使得該錯誤處理系統用起來更爽。今天博客的主題就是系統的搞一下Swift中的錯誤處理,以及看一下Swift中是如何拋出異常的。在編譯型語言中,錯誤通常分爲編譯錯誤和運行時錯誤。咱們平時在代碼中處理的錯誤爲運行時錯誤,咱們對異常進行處理的操做的目的是爲了防止程序出現錯誤而致使其餘的反作用,好比用戶數據未保存等等。html
在今天的博客中,先給出主動產生異常的幾種狀況,而後再給出如何處理被動異常。數組
1、主動退出程序的幾種狀況函數
在Objective-C中,在單元測試時咱們會使用斷言,斷言中條件知足時會產生異常,並打印出相應的斷言錯誤,在Swift中也有幾種產生異常的語法。在本篇博客的第一部分就給出這幾種方法。post
1.Fatal Errors(致命的錯誤)單元測試
使用fatalError()函數能夠當即終止你的應用程序,在fatalError()中能夠給出終止信息。使用fatalError()函數,會毫無條件的終止你的應用程序,用起來也是比較簡單的,就是一個函數的調用。下方這個Demo一目瞭然呢,在此就不作過多贅述了。測試
2. Assertions(斷言)url
在單元測試中是少不了斷言的,Swift中的斷言和Objective-C的區別不是太大,使用方法也是大同小異。下方就是斷言的兩種方法,由代碼提示可知,在斷言中的提示條件是可選的。斷言會在Debug模式下起做用,可是在Release版本中就會被忽略。spa
在assert()函數中, 第一個參數是Bool類型,第二個參數是輸出的信息。當條件爲true時,斷言不執行,相應的斷言信息不打印。當條件爲false時,斷言執行,而且打印相應的斷言信息。debug
assertionFailure()函數只有一個Message參數,而且該參數也是能夠省略的,assertionFailure()沒有條件。以下所示:設計
3. 先決條件(Preconditions)
Preconditions的用法和斷言同樣,不過有一點須要主要,Preconditions在debug和release模式下都會被執行,除非使用–Ounchecked進行編譯。下方截圖是代碼提示給出的Preconditions函數的提示,以下所示:
關於Preconditions的具體用法請參照斷言,和斷言用法同樣,在此就不作過多的贅述了。
二.Swift中的錯誤處理
在Objective-C中,若是你處理過錯誤的話,那麼你將會對NSError很熟悉。在Swift中,若是你要定義你本身的錯誤類型,你只須要實現ErrorType協議便可。聲明完錯誤類型後,就能夠在處理錯誤拋出異常時使用自定義的錯誤類型了。下方將會一步步帶你走完Swift中的錯誤處理的路程。
1.使用枚舉建立錯誤類型
(1).遵循ErrorType協議,自定義錯誤類型。下方定義了一個錯誤類型枚舉,該枚舉遵循了ErrorType協議,在接下來的代碼中咱們將會使用這個MyCustomErrorType枚舉,錯誤枚舉的實現以下所示:
1 //定義錯誤類型 2 enum MyCustomErrorType: ErrorType { 3 case ErrorReason1 4 case ErrorReason2 5 case ErrorReason3 6 }
(2).在咱們的函數定義時可使用throws關鍵字,以及在函數中使用throw關鍵字對錯誤進行拋出,拋出的錯誤類型就可使用上面咱們本身定義的錯誤類型。下方函數就是一個能夠拋出錯誤的函數,拋出的錯誤就是咱們在上面枚舉中所定義的類型。具體代碼以下所示:
1 func myThrowFunc1() throws { 2 3 let test:Int? = nil 4 5 guard test != nil else { 6 throw MyCustomErrorType.ErrorReason1 7 } 8 }
(3).上面函數的功能是對錯誤進行拋出,接下來就該使用do-catch來處理拋出的錯誤。使用try對錯誤進行捕捉,使用do-catch對錯誤進行處理。具體處理方式以下所示。在下方錯誤處理中相似於switch-case語句,catch後邊能夠枚舉匹配錯誤類型,具體以下所示:
(4)在枚舉實現錯誤類型中咱們能夠經過值綁定的形式爲錯誤添加錯誤代碼和錯誤緣由。在聲明枚舉時,咱們使用了枚舉元素值綁定的特性(關於枚舉使用的更多細節請參考以前的博客《窺探Swift之別樣的枚舉類型》)。在聲明枚舉成員ErrorState時,咱們爲其綁定了兩個變量,一個是錯誤代碼errorCode, 另外一個是錯誤緣由errorReason。這二者能夠在拋出錯誤時爲其傳入相應的值,以下方代碼片斷中的throwError函數所示,在拋出錯誤是爲errorCode指定的錯誤代碼爲404,爲errorReason指定的錯誤緣由是「not found」。
最後就是使用do-catch處理異常了,在catch中對綁定的錯誤代碼和錯誤緣由進行了獲取,而且經過where子句進行了錯誤代碼的篩選。此處catch的用法與switch-case中獲取枚舉綁定值的用法是同樣的,因此在此就不作過多的贅述。具體實現方式以下代碼所示:
2.使用結構體爲錯誤處理添加Reason
在上面的內容中,使用枚舉遵循ErrorType協議的方式定義了特定的錯誤類型。接下來咱們將使用結構體來遵循ErrorType協議,爲錯誤類型添加錯誤緣由。也就是說,咱們能夠在拋出錯誤時,給自定義錯誤類型提供錯誤緣由。該功能在開發中是很是經常使用的,並且用起來也是很是爽的。接下來就看一下如何爲咱們的錯誤類型添加錯誤緣由。
(1)使用結構體建立錯誤類型,下方名爲MyErrorType的結構體遵循了ErrorType協議,而且在MyErrorType結構體中,聲明瞭一個reason常量,該reason常量中存儲的就是錯誤緣由,具體實現方式以下:
1 struct MyErrorType: ErrorType { 2 let reason : String 3 }
(2)上面定義完錯誤類型結構體後,在錯誤拋出中就可使用了。在錯誤拋出時,能夠傳入一個錯誤緣由,具體代碼以下所示:
1 func myThrowFunc2() throws { 2 3 let test:Int? = nil 4 5 guard test != nil else { 6 throw MyErrorType(reason: "我是詳細的錯誤緣由,存儲在error中") 7 } 8 }
(3)最後要對拋出的錯誤進行do-catch處理,在處理時,能夠對錯誤緣由進行打印,錯誤緣由存儲在error中,具體操做和打印結果以下所示:
由上面的輸出結果可知,error是咱們自定義的MyErrorType類型,咱們可使用下面的代碼來代替catch中的print語句,以下所示:
上面的作法彷佛有些麻煩,還有一種簡化輸出的方法,就是在上述結構體中實現CustomDebugStringConvertible協議,對描述信息進行一個重寫,就能夠在打印error時,只打印錯誤信息,下方是重寫後的結構體。
1 struct MyErrorType: ErrorType,CustomDebugStringConvertible { 2 let reason : String 3 var debugDescription: String { 4 return "錯誤類型-----\(self.dynamicType): \(reason)" 5 } 6 }
修改後,輸出結果以下,直接打印error輸出的就是錯誤信息,而不是MyErrorType類型。
3.使String類型遵循ErrorType協議,直接使用String提供錯誤緣由
在「2」中,咱們使用告終構體遵循ErrorType協議的形式,來爲錯誤提供錯誤信息的。在接下來的部分,咱們將經過更爲簡單的方式爲拋出的錯誤提供錯誤信息。這種方式更爲簡單,也易於理解,具體方式以下方代碼所示:
3、在錯誤處理中使用內置關鍵字
1.初探這些內置關鍵字
在Swift中提供了一些內置關鍵字(__FILE__, __FUNCTION__, __LINE__等)來獲取上下文信息,在本篇博客的第三部分,將會給出如何在咱們的錯誤處理中使用這些內置關鍵字。下方就是這些內置關鍵字的做用,以下所示:
上面說是內置關鍵字,其實就是存儲代碼上下文的宏定義,上方代碼段簡單的給出了這些內置關鍵字的做用與用法,在接下來將在ErrorType中使用這些內置關鍵字,讓咱們的錯誤信息更加豐富多彩。
2.在ErrorType中使用上述內置關鍵字
若是想在ErrorType中使用這些上下文內置關鍵字,咱們只須要對ErrorType進行擴展,使其在ErrorType提供錯誤信息時給出出錯的上下文信息。固然,這實現起來比較簡單,就是在ErrorType中添加了一個擴展方法contextString()。該方法的做用就是提供錯誤的上下文信息,也就是在出錯的地方,調用contextString()方法生成上下文描述信息便可。對ErrorType協議的具體延展實現以下代碼段所示.
在下方代碼片斷中,咱們對ErrorType進行了擴展,爲ErrorType添加了contextString的函數實現。contextString()函數有三個默認參數,分別是file--當前文件名,function--當前出錯的函數名,line--當前拋出異常的行數。上述三個參數都有參數默認值,分別對應着__FILE__, __FUNCTION__, __LINE__。該擴展函數的返回值爲這三個參數組成從字符串信息。具體實現以下所示:
3.使用擴展的contextString方法
上面咱們使用結構體實現ErrorType協議的形式,爲錯誤類型添加錯誤緣由。接下來咱們將在添加reason的同時,使用contextString()函數添加描述信息。下方CustomErrorType結構體遵循了ErrorType協議,其中添加了一個reason常量來存儲錯誤緣由,一個context常量來存儲上下文信息,而且爲該結構體添加了一個構造函數,在構造函數中初始化和reason常量。具體實現以下所示:
4. 拋出並捕獲異常
在下方代碼中函數throwError()拋出了異常,該拋出的錯誤類型是CustomErrorType。在建立CustomErrorType類型實例,也就是err變量時,咱們指定了錯誤緣由,也就是爲reason賦了一個值。在建立完err實例後,咱們又調用延展contextString()函數獲取異常的上下文信息,並把返回的內容存儲在err實例的context屬性中。最後使用throw關鍵字拋出err實例,以下方第一部分代碼所示。
在建立拋出異常的函數後,咱們須要對拋出的異常進行捕獲。也就是使用try對異常進行捕獲,使用do-catch對異常進行處理,具體操做以下方第二段代碼所示。
5. 分析打印結果
通過上述步驟若是你在Playground中進行試驗的,那麼在控制檯上你將會看到以下信息。從打印出的信息咱們能夠看到,信息包括reason:錯誤緣由,和context:異常上下文。在下方的輸出結果中,文件名咱們能夠看到是<EXPR>這並非確切的文件名,由於咱們是在Playground中使用的,而且不是確切的Swift源文件,因此獲取不到確切的文件名。
爲了觀察確切的文件名,咱們須要在確切的Swift源文件中拋出上述異常。在特定Swift源文件中,咱們會看到下方的輸出結果。從下方的輸出日誌中,咱們能夠清楚的看到文件名是一個詳細的文件路徑。以下所示:
今天的博客內容也夠多的了,就先到這兒吧,之後在作小的Demo時,若是用到其餘的錯誤處理方式,在作詳細介紹呢。