Swift 2.0中的錯(cuò)誤處理

本文翻譯自Error Handling in Swift 2.0

Swift主設(shè)計(jì)師在蘋(píng)果今年的WWDC上發(fā)布Swift 2.0時(shí)指出2.0版本主要提升了語(yǔ)言的三個(gè)方面:fundamentals,safetybeautiful code.除去新特性度陆、改進(jìn)、美化等,其中一條對(duì)你的swift 1.x 代碼有影響的要數(shù)錯(cuò)誤處理了.

因?yàn)槟銦o(wú)法不寫(xiě)錯(cuò)誤處理.想要使用Swift 2.0則必須要有它.而且它和以前在Cocoa和Cocoa Touch中使用NSError方法有所不同.

  • 歷史起源

眾所周知,Swift是被用來(lái)替代Objective-C開(kāi)發(fā)OS X和iOS應(yīng)用的.在早期的版本中,Objective-C并沒(méi)有原生的錯(cuò)誤處理.后來(lái)有了NSException類(lèi)和NS_DURING,NS_HANDLER,NS_ENDHANDLER宏出現(xiàn)時(shí)才有了異常處理.這套方案被稱(chēng)為"經(jīng)典的異常處理",且這些宏是以C函數(shù)中的setjmp()和longjmp()為基礎(chǔ).
捕捉異常的結(jié)構(gòu)如下所示,其中NS_DURING和NS_HANDLER宏內(nèi)拋出的異常會(huì)執(zhí)行NS_HANDEL和NS_ENDHANDLER宏之間的代碼.

NS_DURING
    // Call a dangerous method or function that raises an exception:
    [obj someRiskyMethod];
NS_HANDLER
    NSLog(@"Oh no!");
    [anotherObj makeItRight];
NS_ENDHANDLER

快速獲取異常的方法為(現(xiàn)在仍可用):

- (void)someRiskyMethod
{
    [NSException raise:@"Kablam"
                format:@"This method is not implemented yet. Do not call!"];
}

如你所料,這樣的異常處理方式給早期的Cocoa開(kāi)發(fā)者帶來(lái)很多測(cè)試工作.然而這些程序員仍然可以保持高下巴,因?yàn)樗麄兒苌儆玫剿?在Cocoa和Cocoa Touch中,異常處理被降級(jí)到用來(lái)標(biāo)記災(zāi)難性的、不可恢復(fù)的錯(cuò)誤,例如程序員犯的錯(cuò)誤.上面的-someRiskyMethod就是個(gè)好的例子,是因?yàn)閷?shí)現(xiàn)還沒(méi)準(zhǔn)備好而引發(fā)的一個(gè)異常.Cocoa和Cocoa Touch框架里,用NSError處理的可恢復(fù)錯(cuò)誤會(huì)在待會(huì)討論到.

  • 原生異常處理

我想蘋(píng)果從Objective-C經(jīng)典的異常處理中嘗到了苦頭,于是在OS X 10.3時(shí)發(fā)布了原生異常處理,早于iOS的任何版本.它是通過(guò)基本嫁接C++異常到Objective-C中所完成的.異常處理結(jié)構(gòu)現(xiàn)在看起來(lái)是下面的樣子:

@try {
    [obj someRiskyMethod];
}
@catch (SomeClass *exception) {
    // Handle the error.
    // Can use the exception object to gather information.
}
@catch (SomeOtherClass *exception) {
    // ...
}
@catch (id allTheRest) {
    // ...
}
@finally {
    // Code that is executed whether an exception is thrown or not.
    // Use for cleanup.
}

原生異常處理通過(guò)不同的@catch blocks來(lái)處理不同的異常類(lèi)型.而無(wú)論@try block里的結(jié)果如何都會(huì)執(zhí)行@finally block里的代碼.
雖然提高NSException的使用可以達(dá)到同原生異常處理同樣的效果,但通過(guò)@throw<expression>的方式使拋出的異常更加明確.而NSException的異常卻是由眾多原因?qū)е碌?

  • NSError

雖然原生的異常處理相較于傳統(tǒng)的異常處理有諸多優(yōu)勢(shì),但Cocoa和Cocoa Touch開(kāi)發(fā)者仍然很少用到,僅在不可恢復(fù)的編程錯(cuò)誤時(shí)使用.可恢復(fù)的編程錯(cuò)誤,早期時(shí)用NSError來(lái)處理異常.NSError模式在Swift 1.x時(shí)仍可用.

在Swift 1.x中,Cocoa和Cocoa Touch函數(shù)和方法可能會(huì)失敗,通過(guò)給對(duì)象返回一個(gè)布爾值false或者nil來(lái)表示失敗.此外,NSErrorPointer被當(dāng)做參數(shù)傳遞到返回的錯(cuò)誤信息中.一個(gè)典型的例子如下:

// A local variable to store an error object if one comes back:
var error: NSError?
// success is a Bool:
let success = someString.writeToURL(someURL,
                                    atomically: true,
                                    encoding: NSUTF8StringEncoding,
                                    error: &error)
if !success {
    // Log information about the error:
    println("Error writing to URL: \(error!)")
}

編程錯(cuò)誤可以被標(biāo)記為Swift標(biāo)準(zhǔn)庫(kù)中的fatalError("Error message")方法來(lái)將生成的錯(cuò)誤信息log到控制臺(tái)并無(wú)條件終止執(zhí)行.另外還包括assert(),assertionFailure(),precondition()和preconditionFailure()方法.

當(dāng)Swift首次發(fā)布時(shí),一些Apple平臺(tái)之外的開(kāi)發(fā)者煽風(fēng)點(diǎn)火.他們聲稱(chēng)Swift并不能成為真正的語(yǔ)言,因?yàn)樗狈Ξ惓L幚頇C(jī)制.當(dāng)然,Cocoa和Cocoa Touch開(kāi)發(fā)者知道,有NSError和NSException可用.私以為,蘋(píng)果還在思考實(shí)現(xiàn)error/exception處理的正確方式.且蘋(píng)果推遲了Swift的開(kāi)源,直到問(wèn)題解決.這些障礙隨著Swift 2.0的發(fā)布而被清除.

  • Swift 2.0中的錯(cuò)誤處理

如果你想在Swift 2.0中拋出個(gè)錯(cuò)誤,這個(gè)對(duì)象必須遵從ErrorType協(xié)議.如你所想,NSError遵從這個(gè)協(xié)議.使用枚舉來(lái)定義不同的錯(cuò)誤類(lèi)型.

enum AwfulError: ErrorType {
    case Bad
    case Worse
    case Terrible
}

繼而,一個(gè)函數(shù)或方法通過(guò)關(guān)鍵字throws標(biāo)記處理拋出的一個(gè)或多個(gè)錯(cuò)誤:

func doDangerousStuff() throws -> SomeObject {
    // If something bad happens throw the error:
    throw AwfulError.Bad

    // If something worse happens, throw another error: 
    throw AwfulError.Worse

    // If something terrible happens, you know what to do: 
    throw AwfulError.Terrible

    // If you made it here, you can return:
    return SomeObject()
}

為了獲得這些錯(cuò)誤,一個(gè)新的do-catch語(yǔ)句出現(xiàn)了:

do {
    let theResult = try obj.doDangerousStuff()
}
catch AwfulError.Bad {
    // Deal with badness.
}
catch AwfulError.Worse {
    // Deal with worseness.
}
catch AwfulError.Terrible {
    // Deal with terribleness.
}
catch ErrorType {
    // Unexpected error!
}

do-catch語(yǔ)句有點(diǎn)像switch,通過(guò)詳盡的引起錯(cuò)誤的清單來(lái)捕捉相對(duì)應(yīng)的錯(cuò)誤類(lèi)型.另外注意try關(guān)鍵字的使用,用來(lái)標(biāo)記拋出異常的代碼位置,以使你在閱讀代碼的時(shí)候知道哪處代碼容易出錯(cuò).
關(guān)鍵字try的另一種用法為try!.該關(guān)鍵字適用于程序員的再次錯(cuò)誤.如果你標(biāo)記一個(gè)異常call為try!,表示告訴編譯器這個(gè)錯(cuò)誤將永不會(huì)出現(xiàn),不需要捕捉到.如果在此出現(xiàn)了錯(cuò)誤,則程序?qū)⑼V惯\(yùn)行,你需要開(kāi)始debug.

let theResult = try! obj.doDangerousStuff()
  • 與Cocoa和Cocoa Touch框架的交互

現(xiàn)在的問(wèn)題是,如何在Swift 2.0中處理爺爺輩的NSError API?蘋(píng)果已經(jīng)在Swift 2.0中做了統(tǒng)一的工作,并且做好了將來(lái)用Swift寫(xiě)框架的準(zhǔn)備.
Cocoa和Cocoa Touch函數(shù)和方法產(chǎn)生的NSError時(shí)會(huì)自動(dòng)轉(zhuǎn)換為Swift新的錯(cuò)誤處理機(jī)制.
例如,下面的NSString的初始化在Swift 1.x中有以下signature:

convenience init?(contentsOfFile path: String,
                  encoding enc: UInt,
                  error error: NSErrorPointer)

Swift 2.0中signature轉(zhuǎn)換為:

convenience init(contentsOfFile path: String,
                 encoding enc: UInt) throws

注意到在Swift 2.0中,初始化不再被標(biāo)記為可以失敗,且不需要一個(gè)NSErrorPointer參數(shù),并有個(gè)throws來(lái)標(biāo)記可能存在錯(cuò)誤.使用新的signature的例子如下:

do {
    let str = try NSString(contentsOfFile: "Foo.bar",
                           encoding: NSUTF8StringEncoding)
}
catch let error as NSError {
    print(error.localizedDescription)
}

可以看到錯(cuò)誤發(fā)生時(shí)產(chǎn)生了一個(gè)NSError信息,此時(shí)你可以使用你所熟知的API了.實(shí)際上,任何ErrorType都能被轉(zhuǎn)換為NSError.

  • 最后來(lái)談?wù)凘finally?

細(xì)心的讀者也許注意到在Swift 2.0中介紹的新的語(yǔ)句為do-catch,而不是do-catch-finally.當(dāng)你需要執(zhí)行不論是否發(fā)生錯(cuò)誤都要執(zhí)行的語(yǔ)句時(shí)怎么辦?為了解決這個(gè)問(wèn)題,現(xiàn)在你可以使用defer語(yǔ)句來(lái)延遲執(zhí)行一個(gè)block代碼直到當(dāng)前作用域退出.

// Some scope:
{
    // Get some resource.

    defer {
        // Release resource.
    }

    // Do things with the resource.
    // Possibly return early if an error occurs.

} // Deferred code is executed at the end of the scope.

Swift 2.0在融合以前的Cocoa和Cocoa Touch錯(cuò)誤處理外使其進(jìn)入了一個(gè)使眾多開(kāi)發(fā)者更加熟悉的現(xiàn)代化的操作方式.統(tǒng)一的操作方式使Swift語(yǔ)言和它所繼承的框架能夠獲得良好的發(fā)展.

Girl學(xué)iOS100天 第22天

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌黔寇,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件螃诅,死亡現(xiàn)場(chǎng)離奇詭異啡氢,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)术裸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)倘是,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人袭艺,你說(shuō)我怎么就攤上這事搀崭。” “怎么了猾编?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵瘤睹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我答倡,道長(zhǎng)轰传,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任瘪撇,我火速辦了婚禮获茬,結(jié)果婚禮上港庄,老公的妹妹穿的比我還像新娘。我一直安慰自己恕曲,他們只是感情好鹏氧,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著佩谣,像睡著了一般把还。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上茸俭,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天吊履,我揣著相機(jī)與錄音,去河邊找鬼瓣履。 笑死率翅,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的袖迎。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼腺晾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼燕锥!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起悯蝉,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤归形,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后鼻由,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體暇榴,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年蕉世,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蔼紧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡狠轻,死狀恐怖奸例,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情向楼,我是刑警寧澤查吊,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站湖蜕,受9級(jí)特大地震影響逻卖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜昭抒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一评也、第九天 我趴在偏房一處隱蔽的房頂上張望虚茶。 院中可真熱鬧,春花似錦仇参、人聲如沸嘹叫。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)罩扇。三九已至,卻和暖如春怕磨,著一層夾襖步出監(jiān)牢的瞬間喂饥,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工肠鲫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留员帮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓导饲,卻偏偏與公主長(zhǎng)得像捞高,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子渣锦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)硝岗、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,022評(píng)論 4 62
  • 破舊的棉襖擋不住冬日里的寒風(fēng)和雨雪袋毙,咽下石子的干啞的喉嚨使他不肯發(fā)出一聲型檀。他饑餓,寒冷听盖,卻是一天中最?lèi)芤獾臅r(shí)候胀溺。 ...
    割曉閱讀 297評(píng)論 0 1
  • 乙一 1 我之所以離開(kāi)家、一個(gè)人過(guò)日子皆看,純粹只是因?yàn)槲蚁胍粋€(gè)人獨(dú)處仓坞。我迫切地希望前往一 個(gè)沒(méi)有人認(rèn)識(shí)我的陌生地方,...
    暗夜如歌閱讀 638評(píng)論 0 0
  • 抑郁癥病人的世界悬蔽,是什么顏色扯躺,是夜晚的顏色,因?yàn)橐粋€(gè)又一個(gè)夜蝎困,不能入睡录语。 就在去年,我發(fā)現(xiàn)自己不會(huì)笑了禾乘,是真的澎埠。說(shuō)...
    吳森迪閱讀 136評(píng)論 0 0
  • HTML、XML始藕、XHTML 有什么區(qū)別蒲稳? HTML(HyperText Mark-up Language)即[超...
    饑人谷_Dylan閱讀 674評(píng)論 0 50