對比 attempt、try? 和 try!

作者:Erica Sadun,原文鏈接,原文日期:2016-03-15
譯者:星夜暮晨;校對:numbbbbb;定稿:CMB

在 Swift 中,try? 關鍵字將一個可能會拋出錯誤的調用轉變為一個可選值。它會返回成功值 (.some(T))或 nil (.none)。使用 try? 允許您在 guard 語句中使用會拋出錯誤的代碼,還允許您中斷錯誤處理鏈并離開當前作用域,然后進入專門用于處理成功狀態閉包的條件綁定當中。

try? 有其優缺點。它允許您創建行為可預見的代碼,尤其是在結束閉包和 Playground 當中,但是忽視錯誤對于開發者來說往往是不可取的。

前一陣子,我寫了 實現可打印內容的 try? 和 try!。它在保存錯誤信息和使用基于可選值的控制語句之間提供了一個平衡點。

隨著時間的推移,我開始逐步調整我的這個做法,嘗試去完善和精簡我的想法。我的目的是:重現 try? 和其 fatal-error 版本(try!) 的所有行為,并保留錯誤處理功能。我將這個想法稱之為“多用途的替換嘗試”。

我的 attempt 函數會被動接收拋出語句運行時候的上下文狀態,然后提供一個默認可打印的錯誤處理器。如果您將 crashOnError 設為 true,這個 attempt 語句就會表現得和 try! 一樣,但是只有在造成程序崩潰的錯誤以及問題發生的源代碼位置打印之后,才會中止程序的執行。

最新的 attempt 組件是自定義錯誤處理器,默認會進行打印。這可能有點小題大做,不過我很喜歡這個想法,因為我可以按需修改錯誤處理器的行為。如果您只希望添加錯誤清理功能,然后再打印錯誤的話,那么您可以從自定義的錯誤處理器中調用默認的處理器。

單獨使用的 attempt 如下所示,它帶有一個尾閉包:

attempt {
   let mgr = NSFileManager.defaultManager()
   try mgr.createDirectoryAtPath(
       "/Users/notarealuser",
       withIntermediateDirectories: true,
       attributes: nil)
}

但是,需要結合 guard 調用時,由于不能夠在尾隨閉包中將 guard 所需要的 else 閉包和 attempt 閉包區分開來,因此需要使用一個更為傳統的參數方法。

guard let fileContents = attempt(closure: {
    _ -> [NSURL] in
    let url = NSURL(fileURLWithPath: "/Users/notarealuser")
    let mgr = NSFileManager.defaultManager()
    return try mgr.contentsOfDirectoryAtURL(
            url, includingPropertiesForKeys: nil, 
            options: [])
}) else { fatalError("failed") }

attempt 的相關代碼如下所示,您也可以在 GitHub 上訪問內容更豐富的 CoreError.swift 代碼實現,它當中還增加(更新)了語境化的實用工具 (Contextualization Utilities)。因為 Swift 將會采用更現代化的調試標識符,因此這種尖叫蛇式大寫 (SCREAMING_SNAKE_CASE,譯者注:如您所見,就是在大寫單詞之間加上下劃線) 的常量將很快被移除。

/// 由文件路徑 (File Path)、行數 (Line Number) 和錯誤元組 (Error Tuple) 組成
public typealias CommonErrorHandlerType = (String, Int, ErrorType) -> Void

/// 可以打印上下文和錯誤的默認錯誤處理器
public let defaultCommonErrorHandler: CommonErrorHandlerType = {
    filePath, lineNumber, error in
    let trimmedFileName: String = (filePath as NSString).lastPathComponent
    print("Error \(trimmedFileName):\(lineNumber) \(error)")
}

/// 引入錯誤處理器來替代 `try?`
/// 默認的錯誤處理器將會在返回 nil 之前打印錯誤信息
public func attempt<T>(
file fileName: String = __FILE__,
line lineNumber: Int = __LINE__,
    crashOnError: Bool = false,
    errorHandler: CommonErrorHandlerType = defaultCommonErrorHandler,
// 感謝 http://twitter.com/Kametrixom/status/709809975447707648
@noescape closure: () throws -> T) -> T? { 

do {
// 只有當閉包成功的時候,才會返回執行,這會返回 T
return try closure()
        } catch {
// 通過崩潰來模仿 try!
if crashOnError {
print("Fatal error \(fileName):\(lineNumber): \(error)")
                fatalError()
            }
// 運行錯誤處理器,并返回 nil
            errorHandler(fileName, lineNumber, error)
return nil
        }
}

一如既往,如果您發現了任何問題,或者有任何建議的話,請聯系我,讓我知曉。

本文由 SwiftGG 翻譯組翻譯,已經獲得作者翻譯授權,最新文章請訪問 http://swift.gg。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容