不算“真正的语言”?详说Swift 2.0中的错误处理

苹果公司在今年的全球开发者大会(Worldwide Developers Conference, WWDC)上宣布推出Swift2.0,该语言的首席架构师Chris Lattner表示,Swift 2.0主要在语言基本语法、安全性和格式美观度这三方面进行了改进。除了这些新的功能特性,还有对语法的优化、修饰及美化,最后是Swift 1.x中最具影响力的错误处理机制。

这是因为你根本无法回避它。如果打算使用Swift 2.0的话,必须接受错误处理这样的机制,并且错误处理机制将改变Cocoa和Cocoa Touch框架中使用NSError与方法交互的方式。

历史一瞬:不起眼的开端

我们都知道,Swift语言作为Objective-C当前替代语言被推出,是OS X和iOS应用程序开发的“通用语”。在最初的版本中,Objective-C没有原生的异常处理机制。后来通过添加NSException类,还有 NS_DURING, NS_HANDLER和 NS_ENDHANDLER宏才有了异常处理。这种方案现在被称为“经典的异常处理”,还有这些宏都是基于setjmp()和longjmp()这两个C语言函数的。

异常捕获(exception-catching)看起来如下所示,在NS_DURING和NS_HANDLER宏之间抛出的任何异常都将会导致在NS_HANDLER和NS_ENDHANDLER宏之间执行相应的代码。

[cpp] view plaincopy

  1. NS_DURING
  2. // Call a dangerous method or function that raises an exception:
  3. [obj someRiskyMethod];
  4. NS_HANDLER
  5. NSLog(@"Oh no!");
  6. [anotherObj makeItRight];
  7. NS_ENDHANDLER

下面是立刻能触发抛出异常的方法(现在仍然可用):

[cpp] view plaincopy

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

可以想象,这种手工处理异常的方式戏弄的是早期Cocoa框架程序开发人员。但是这些程序员还不至于到这份儿上,因为他们很少使用这种方式。无论在Cocoa还是Cocoa Touch框架下,异常通常都被归为灾难性的,不可恢复的错误,比如程序员造成的错误。上面的-someRiskyMethod就是很好的例子,由于实现部分没有准备好而引发了异常。在Cocoa和Cocoa Touch框架中,可恢复的错误由稍后讨论的NSError类来处理。

原生的异常处理

我想由于Objective-C中的经典异常处理机制对应的手工处理方式让人感觉闹心,于是苹果公司在Mac OS X 10.3(2003年10月)中发布了原生的异常处理机制,彼时还没有iOS系统。这本质上是将C++的异常处理嫁接到了Objective-C。异常处理的结构目前看起来是这样的:

[cpp] view plaincopy

  1. @try {
  2. [obj someRiskyMethod];
  3. }
  4. @catch (SomeClass *exception) {
  5. // Handle the error.
  6. // Can use the exception object to gather information.
  7. }
  8. @catch (SomeOtherClass *exception) {
  9. // ...
  10. }
  11. @catch (id allTheRest) {
  12. // ...
  13. }
  14. @finally {
  15. // Code that is executed whether an exception is thrown or not.
  16. // Use for cleanup.
  17. }

原生的异常处理使你有机会为每个异常类型指定不同@catch部分。无论@try结果如何,@finally都要执行其对应的代码。

尽管原生的异常处理如所预期的那样抛出一个NSException异常,但是最明确的方法还是“@throw <expression>;”语句。通常你抛出的是NSException实例,但说不定什么对象会被抛出。

NSError

尽管Objective-C原生与经典的异常处理有许多优点,但Cocoa和Cocoa Touch框架应用程序开发人员仍然很少使用异常,而是限制程序出现程序员所导致的不可恢复的错误。使用NSError类处理可恢复的错误,这种方法早于使用异常处理。Swift 1.x也继承了NSError的样式。

在Swift 1.x中,Cocoa和Cocoa Touch的方法和函数可能不会返回一个布尔类型的false或者nil来表示一个失败(failure)的对象。另外,NSErrorPointer对象会被当作一个参数返回特定的失败信息。下面是个典型的例子:

[cpp] view plaincopy

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

程序员所导致的错误可以用Swift标准库(Swift Standard Library)函数fatalError("Error message”)来标记,将其在控制台记录为错误消息并无条件中止执行。还可以使用assert(), assertionFailure(), precondition()和preconditionFailure()这些函数。

Swift第一次发布时,一些非苹果平台开发人员已经准备好了火把和干草叉。他们声称Swift不能算是“真正的语言”,因为它缺乏异常处理。但是,Cocoa和Cocoa Touch社区对此不予理睬,我们知道NSError和NSException那个时候就存在了。就我个人而言,我相信苹果公司仍然在思考实现错误和异常处理的正确方式。我还认为直到问题解决了,苹果公司才会公开Swift源码。这一切问题在Swift 2.0中全被扫清了。

Swift 2.0中的错误处理

在Swift 2.0中,如果想要抛出错误,那么抛出的对象必须符合ErrorType协议。可能正如你所愿,NSError就符合该协议。枚举在这里用来给错误进行分类。

[cpp] view plaincopy

  1. enum AwfulError: ErrorType {
  2. case Bad
  3. case Worse
  4. case Terrible
  5. }

然后如果一个可能抛出一个或多个错误的函数或方法会被throws关键字标记:

[cpp] view plaincopy

  1. func doDangerousStuff() throws -> SomeObject {
  2. // If something bad happens throw the error:
  3. throw AwfulError.Bad
  4. // If something worse happens, throw another error:
  5. throw AwfulError.Worse
  6. // If something terrible happens, you know what to do:
  7. throw AwfulError.Terrible
  8. // If you made it here, you can return:
  9. return SomeObject()
  10. }

为了捕获错误,新型的do-catch语句出现了:

[cpp] view plaincopy

  1. do {
  2. let theResult = try obj.doDangerousStuff()
  3. }
  4. catch AwfulError.Bad {
  5. // Deal with badness.
  6. }
  7. catch AwfulError.Worse {
  8. // Deal with worseness.
  9. }
  10. catch AwfulError.Terrible {
  11. // Deal with terribleness.
  12. }
  13. catch ErrorType {
  14. // Unexpected error!
  15. }

这个do-catch语句和switch语句有一些相似之处,被捕获的错误详尽无遗,因此你可以使用这种样式来捕获抛出的错误。还要注意关键字try的使用。它是为了明确地标示抛出的代码行,因此当阅读代码的时候,你能够立刻找到错误在哪里。

关键字try的变体是“try!”。这个关键字大概也适用于那些程序员导致的错误。如果使用“try!”标记一个被调用的抛出对象中的方法,你等于告诉编译器这个错误永远不会发生,并且你也不需要捕获它。如果该语句本身产生了错误(error),应用程序会停止执行,那么你就要开始调试了。

[cpp] view plaincopy

  1. let theResult = try! obj.doDangerousStuff()

与Cocoa和Cocoa Touch框架间的交互

现在的问题是,你如何在Swift 2.0中处理爷爷级的NSError API呢?苹果公司已经在Swift 2.0中为统一代码行为作了大量工作,并且已经为未来写入Swift的框架准备方法。Cocoa和Cocoa Touch中可以产生NSError实例的方法和函数有苹果公司的签名( signature),可以自动转换为Swift新的错误处理方式。

例如,这个NSString的构造器( initializer)在Swift 1.x中就有以下签名:

[cpp] view plaincopy

  1. convenience init?(contentsOfFile path: String,
  2. encoding enc: UInt,
  3. error error: NSErrorPointer)

Swift 2.0中,签名被转换成:

[js] view plaincopy

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

注意:在Swift 2.0中,构造器不再被标记为failable,它并不需要NSErrorPointer来做参数,而是使用抛出异常的方式显式地指示潜在的失败。

下面的例子使用了这种新的签名:

[cpp] view plaincopy

  1. do {
  2. let str = try NSString(contentsOfFile: "Foo.bar",
  3. encoding: NSUTF8StringEncoding)
  4. }
  5. catch let error as NSError {
  6. print(error.localizedDescription)
  7. }

注意错误是如何被捕获的,并且如何被转换成了一个NSError实例,这样你就可以获取与其相似API的信息了。事实上,任何ErrorType类型的实例都可以转换成NSError类型。

最后说说@finally

细心的读者可能已经注意到,Swift 2.0引入了一个新的do-catch语句,而不是do-catch-finally。不管是否捕捉到错误的情况下,你如何指定必须运行的代码呢?为此,现在可以使用defer语句,用来推迟代码块的执行直到当前的作用域结束。

[cpp] view plaincopy

  1. // Some scope:
  2. {
  3. // Get some resource.
  4. defer {
  5. // Release resource.
  6. }
  7. // Do things with the resource.
  8. // Possibly return early if an error occurs.
  9. } // Deferred code is executed at the end of the scope.

Swift 2.0将Cocoa和Cocoa Touch的错误处理机制凝聚为具有现代风格的用法,这是一项伟大的工作,也会使许多程序员倍感亲切。统一行为是不错的定位,会使Swift语言和其所继承的框架逐步发展。

(翻译/白云鹏 友情审校/汪洋)

文章来源:Big Nerd Ranch

作者简介:Juan Pablo Claude,来自智利首都圣地亚哥,毕业于美国北卡罗莱纳大学教堂山分校(University of North Carolina at Chapel Hill),获化学博士学位,后入阿拉巴马大学伯明翰分校(University of Alabama at Birmingham)任教。2005年底作为Cocoa和Django框架程序开发人员加入Big Nerd Ranch。此前,有过DOS环境下C编程,Windows环境下使用C++编写数据分析应用程序的经历。

译者简介:白云鹏,移动应用开发者,个人博客:http://baiyunpeng.com



 

时间: 2024-08-04 00:45:59

不算“真正的语言”?详说Swift 2.0中的错误处理的相关文章

《从零开始学Swift》学习笔记(Day 7)——Swift 2.0中的print函数几种重载形式

原创文章,欢迎转载.转载请注明:关东升的博客 Swift 2.0中的print函数有4种重载形式: print(_:).输出变量或常量到控制台,并且换行. print(_:_:).输出变量或常量到指定类型的流中,并且换行. print(_:appendNewline:).输出变量或常量到控制台,appendNewline参数是布尔值,true表示换行,false表示不换行. print(_:_:appendNewline:).输出变量或常量指定类型的流中,appendNewline参数是布尔值,

Swift 2.0学习笔记(Day 7)——Swift 2.0中的print函数几种重载形式

原创文章,欢迎转载.转载请注明:关东升的博客 Swift 2.0中的print函数有4种重载形式: print(_:).输出变量或常量到控制台,并且换行. print(_:_:).输出变量或常量到指定类型的流中,并且换行. print(_:appendNewline:).输出变量或常量到控制台,appendNewline参数是布尔值,true表示换行,false表示不换行. print(_:_:appendNewline:) .输出变量或常量指定类型的流中,appendNewline参数是布尔值

读懂Swift 2.0中字符串设计思路的改变

Swift提供了一种高性能的,兼容Unicode编码的String实现作为标准库 的一部分.在Swift2中,String类型不再遵守CollectionType协议.在以前,String类型是字符的一个集合,类似于数组.现 在,String类型通过一个characters属性来提供一个字符的集合. 为 什么会有这样的变化呢?虽然模拟一个字符串作为字符的集合看起来非常自然,但是String类型与真正的集合类如Array.Set以及 Dictionnary等类型表现得完全不同.这是一直都存在的,但

Swift 2.0 到底「新」在哪?

[编者按]2015年6月,一年一度的苹果 WWDC 大会如期而至,在大会上苹果发布了 Swift 2.0,引入了很多新的特性,以帮助开发者更快.更简单地构建应用.本篇文章作者是 Maxime defauw ,本文中 Maxime 向大家简要介绍 Swift 2.0 中值得注意的新特性.本文系 OneAPM 工程师编译整理. 一年前,苹果推出了面向 iOS 和 OS X 的全新编程语言-- Swift.当听到它发布的时候,像千千万万 iOS 开发者那样,笔者的内心激动无比.正如宣传所说的那样,作为

Swift 2.0 异常处理

WWDC 2015 宣布了新的 Swift 2.0. 这次重大更新给 Swift 提供了新的异常处理方法.这篇文章会主要围绕这个方面进行讨论. 如何建造异常类型? 在 iOS 开发当中,我们会面对很多异常处理.在 Cocoa Touch 中我们使用 NSError 来进行异常处理.在新的 Swift 2.0 中,我们可以使用新的 ErrorType protocol. 在 Swift 中, enum 是最好的方法建立属于你自己的异常类型,你只要在你的 enum 中确认新的 ErrorType.

[转]Swift 2.0初探:值得注意的新特性

转自http://www.cocoachina.com/swift/20150623/12231.html 转眼间,Swift已经一岁多了,这门新鲜.语法时尚.类型安全.执行速度更快的语言已经渐渐的深入广大开发者的心.我同样也是非常喜爱这门新的编程语言. 今年6月,一年一度的WWDC大会如期而至,在大会上Apple发布了Swift 2.0,引入了很多新的特性,以帮助开发者能更快,更简单的构建应用.我在这里也说道说道Swift 2.0中值得大家注意的新特性. guard语句 guard语句和if语

Swift 2.0初探:值得注意的新特性

转眼间,Swift已经一岁多了,这门新鲜.语法时尚.类型安全.执行速度更快的语言已经渐渐的深入广大开发者的心.我同样也是非常喜爱这门新的编程语言. 今年6月,一年一度的WWDC大会如期而至,在大会上Apple发布了Swift 2.0,引入了很多新的特性,以帮助开发者能更快,更简单的构建应用.我在这里也说道说道Swift 2.0中值得大家注意的新特性. guard语句 guard语句和if语句有点类似,都是根据其关键字之后的表达式的布尔值决定下一步执行什么.但与if语句不同的是,guard语句只会

iOS开发——新特性OC篇&amp;Swift 2.0新特性

Swift 2.0新特性 转眼间,Swift已经一岁多了,这门新鲜.语法时尚.类型安全.执行速度更快的语言已经渐渐的深入广大开发者的心.我同样也是非常喜爱这门新的编程语言. 今年6月,一年一度的WWDC大会如期而至,在大会上Apple发布了Swift 2.0,引入了很多新的特性,以帮助开发者能更快,更简单的构建应用.我在这里也说道说道Swift 2.0中值得大家注意的新特性. guard语句 guard语句和if语句有点类似,都是根据其关键字之后的表达式的布尔值决定下一步执行什么.但与if语句不

iOS开发——新特性Swift篇&amp;Swift 2.0 异常处理

Swift 2.0 异常处理 WWDC 2015 宣布了新的 Swift 2.0. 这次重大更新给 Swift 提供了新的异常处理方法.这篇文章会主要围绕这个方面进行讨论. 如何建造异常类型? 在 iOS 开发当中,我们会面对很多异常处理.在 Cocoa Touch 中我们使用 NSError 来进行异常处理.在新的 Swift 2.0 中,我们可以使用新的 ErrorType protocol. 在 Swift 中, enum 是最好的方法建立属于你自己的异常类型,你只要在你的 enum 中确