2015-06-08 32 views
117

我試試看了解SWIFT 2.這裏新的錯誤處理的事情是我做的:我首先聲明一個錯誤枚舉:斯威夫特DO-的try-catch語法

enum SandwichError: ErrorType { 
    case NotMe 
    case DoItYourself 
} 

然後,我宣佈一個方法拋出一個錯誤(不是一個例外人,這是一個錯誤。)。這是該方法:

func makeMeSandwich(names: [String: String]) throws -> String { 
    guard let sandwich = names["sandwich"] else { 
     throw SandwichError.NotMe 
    } 

    return sandwich 
} 

問題是從調用方。這裏是調用此方法的代碼:

let kitchen = ["sandwich": "ready", "breakfeast": "not ready"] 

do { 
    let sandwich = try makeMeSandwich(kitchen) 
    print("i eat it \(sandwich)") 
} catch SandwichError.NotMe { 
    print("Not me error") 
} catch SandwichError.DoItYourself { 
    print("do it error") 
} 

do行編譯後說:Errors thrown from here are not handled because the enclosing catch is not exhaustive。但在我看來,它是詳盡無遺的,因爲在SandwichError枚舉中只有兩種情況。

對於普通的switch語句,swift可以理解當每個case處理時它都是詳盡無遺的。

+3

你做不指定你拋出的錯誤類型,所以Swift無法確定所有可能的選項 –

+0

有沒有辦法指定錯誤的類型? – mustafa

+0

我在新版本的Swift書中找不到任何東西 - 現在只有關鍵字 –

回答

214

有到斯威夫特2錯誤處理模式很重要的兩點:全面性和彈性。總之,它們歸結爲您需要捕捉每個可能錯誤的聲明,而不僅僅是您知道可以拋出的錯誤。

請注意,您不會聲明函數可以拋出什麼類型的錯誤,而只是拋出它是否拋出。這是一個無窮無盡的問題:作爲爲其他人(包括未來自己)定義功能的人,您不希望讓功能的每個客戶都能夠適應您的實施過程中的每一個變化函數,包括它可能拋出的錯誤。你希望調用你的函數的代碼能夠適應這種變化。

因爲你的函數不能說出它會拋出什麼樣的錯誤(或者可能拋出未來),所以捕獲錯誤的catch塊不知道它會拋出什麼類型的錯誤。所以,除了處理你所知道的錯誤類型之外,你還需要處理那些你不用通用的catch語句的方式 - 這樣,如果你的函數改變了它將來拋出的一組錯誤,調用者仍然會捕獲其錯誤。

do { 
    let sandwich = try makeMeSandwich(kitchen) 
    print("i eat it \(sandwich)") 
} catch SandwichError.NotMe { 
    print("Not me error") 
} catch SandwichError.DoItYourself { 
    print("do it error") 
} catch let error { 
    print(error.localizedDescription) 
} 

但是,讓我們不要止步於此。再想一想這種韌性理念。您設計三明治的方式,必須在每個使用它們的地方描述錯誤。這意味着,無論何時您更改一組錯誤案例,您都必須更改每個使用它們的地方......不是很有趣。

定義自己的錯誤類型背後的想法是讓你集中這樣的事情。你可以定義一個description方法爲你的錯誤:

extension SandwichError: CustomStringConvertible { 
    var description: String { 
     switch self { 
      case NotMe: return "Not me error" 
      case DoItYourself: return "Try sudo" 
     } 
    } 
} 

然後你的錯誤處理代碼可以問問你的錯誤類型來描述自己 - 現在每一個地方,你處理錯誤可以使用相同的代碼的地方,並處理可能出現的未來錯誤情況也是如此。

do { 
    let sandwich = try makeMeSandwich(kitchen) 
    print("i eat it \(sandwich)") 
} catch let error as SandwichError { 
    print(error.description) 
} catch { 
    print("i dunno") 
} 

這也鋪平了道路的錯誤類型(或者它們的擴展),以支持報告錯誤的其他方式 - 例如,你可以有你的錯誤類型,知道如何提出一個UIAlertController延期向iOS用戶報告錯誤。

+0

他的代碼適用於我,沒有任何錯誤或警告。它是詳盡的。 – Fogmeister

+1

@rickster:你真的能重現編譯器錯誤嗎?原始代碼爲我編譯沒有錯誤或警告。如果拋出一個未捕獲的異常,程序就會以'main()'中捕獲的錯誤中止.-因此,雖然你所說的所有內容聽起來都很明智,但我不能重現這種行爲。 –

+5

愛你如何在擴展中分離錯誤消息。真的很好的方式來保持您的代碼清潔!很好的例子! – Konrad77

4

斯威夫特是擔心你的case語句沒有涵蓋所有的情況下,要解決它,你需要創建一個默認的情況下:

do { 
    let sandwich = try makeMeSandwich(kitchen) 
    print("i eat it \(sandwich)") 
} catch SandwichError.NotMe { 
    print("Not me error") 
} catch SandwichError.DoItYourself { 
    print("do it error") 
} catch Default { 
    print("Another Error") 
} 
+2

但是這不是尷尬嗎?我只有兩種情況,所有這些都在'catch'語句中列出。 – mustafa

+0

這是蘋果試圖超級安全,因爲將來你可以在數組中添加一個新條目,並忘記添加case case – Icaro

+2

現在是增加'func method()throws()的增強請求的好時機(YourErrorEnum )',甚至是'拋出(YourEnum.Error1,.Error2,.Error3)',所以你知道可以拋出什麼 –

23

我懷疑這只是尚未正確實施。 Swift Programming Guide肯定似乎意味着編譯器可以推斷窮舉匹配「像switch語句」。它沒有提到需要一般的catch以便詳盡。

您還會注意到錯誤出現在try行上,而不是塊的末尾,即某些時候,編譯器將能夠確定塊中的哪個try語句具有未處理的異常類型。

雖然文檔有點不明確。我已經瀏覽了「Swift中的新內容」視頻,並且找不到任何線索;我會繼續嘗試。

更新:

我們現在最多Beta 3的沒有一絲錯誤類型推斷的。我現在認爲,如果這是有計劃的(我仍然認爲它在某種程度上),協議擴展的動態調度可能會將其殺死。

測試版4更新:

的Xcode爲Throws: 7B4添加文檔註釋的支持,這「應該被用來記錄什麼錯誤可以拋出爲什麼」。我想這至少提供了一些機制來向API消費者傳達錯誤。當你有文檔時誰需要一個類型系統!

另一個更新:

花一些時間希望自動ErrorType推斷,並制定出限制將是模型的什麼之後,我改變了主意 - this是我希望蘋果實現,而不是。本質:

// allow us to do this: 
func myFunction() throws -> Int 

// or this: 
func myFunction() throws CustomError -> Int 

// but not this: 
func myFunction() throws CustomErrorOne, CustomErrorTwo -> Int 

然而,另一個更新

蘋果的錯誤處理的理由現已here。在swift-evolution郵件列表上也有一些有趣的討論。從本質上講,約翰麥考爾反對輸入錯誤,因爲他認爲大多數圖書館最終都會包含一個通用的錯誤案例,而且除了樣板文件(他使用術語'理想虛張聲勢')之外,該類型錯誤不太可能增加代碼。 Chris Lattner表示,如果能夠在彈性模型中工作,他可以​​在Swift 3中輸入錯誤。

+2

很好的回答和更新!這是我們需要的知識/見解! – Klaas

+0

感謝您的鏈接。約翰雖然沒有說服:「許多圖書館包括」其他錯誤「類型」並不意味着每個人都需要一個「其他錯誤」類型。 –

+0

顯而易見的情況是,沒有簡單的方法知道函數會拋出什麼樣的錯誤,直到它發生,迫使開發人員抓住所有錯誤並儘可能最好地處理它們。坦率地說,這很煩人。 –

3

我也對缺少函數可以拋出的類型感到失望,但是現在我得到了@ rickster的感謝,我將這樣總結:假設我們可以指定函數拋出的類型,是這樣的:

enum MyError: ErrorType { case ErrorA, ErrorB } 

func myFunctionThatThrows() throws MyError { ...throw .ErrorA...throw .ErrorB... } 

do { 
    try myFunctionThatThrows() 
} 
case .ErrorA { ... } 
case .ErrorB { ... } 

的問題是,即使我們不改變myFunctionThatThrows什麼,如果我們只是添加了錯誤的情況下,以MyError:

enum MyError: ErrorType { case ErrorA, ErrorB, ErrorC } 

我們擰,因爲我們做/ try/catch不再是窮盡的,也不是我們稱之爲f的任何其他地方拋出MyError

+2

不知道我跟着你爲什麼搞砸了。你會得到一個編譯器錯誤,這是你想要的,對吧?如果您添加枚舉大小寫,那麼會發生切換語句。 – Sam

+0

從某種意義上說,這對於錯誤枚舉/大小寫來說很可能會發生,但是它與enums/switch中的情況完全相同,您是對的。我仍然試圖說服自己,蘋果選擇不輸入我們扔的東西是好的,但是你不幫助我解決這個問題! ^^ – greg3z

+0

手動輸入拋出的錯誤最終會在非平凡的情況下變成一團糟。這些類型是函數中所有throw和try語句所有可能錯誤的聯合。如果你手動維護錯誤枚舉,這將是痛苦的。儘管如此,每個區塊底部的「catch {}」都可能會變得更糟。我希望編譯器最終能夠自動推斷錯誤類型,但是我無法確認。 – Sam

0

unctions這樣創建枚舉:

//Error Handling in swift 
enum spendingError : Error{ 
case minus 
case limit 
} 

創建方法,如:

func calculateSpending(morningSpending:Double,eveningSpending:Double) throws ->Double{ 
if morningSpending < 0 || eveningSpending < 0{ 
    throw spendingError.minus 
} 
if (morningSpending + eveningSpending) > 100{ 
    throw spendingError.limit 
} 
return morningSpending + eveningSpending 
} 

現在檢查的錯誤是存在的或不和處理:

do{ 
try calculateSpending(morningSpending: 60, eveningSpending: 50) 
} catch spendingError.minus{ 
print("This is not possible...") 
} catch spendingError.limit{ 
print("Limit reached...") 
}