在Swift 2.0版本中,Swift语言对其错误处理进行了新的设计,当然了,重新设计后的结果使得该错误处理系统用起来更爽。今天的主题就是系统的搞一下Swift中的错误处理,以及看一下Swift中是如何抛出异常的。在编译型语言中,错误一般分为编译错误和运行时错误。我们平时在代码中处理的错误为运行时错误,我们对异常进行处理的操作的目的是为了防止程序出现错误而导致其他的副作用,比如用户数据未保存等等。
在今天的文章中,先给出主动产生异常的几种情况,然后再给出如何处理被动异常。
一、主动退出程序的几种情况
在Objective-C中,在单元测试时我们会使用断言,断言中条件满足时会产生异常,并打印出相应的断言错误,在Swift中也有几种产生异常的语法。在本篇博客的第一部分就给出这几种方法。
1.Fatal Errors(致命的错误)
使用fatalError()函数可以立即终止你的应用程序,在fatalError()中可以给出终止信息。使用fatalError()函数,会毫无条件的终止你的应用程序,用起来也是比较简单的,就是一个函数的调用。下方这个Demo一目了然呢,在此就不做过多赘述了。
2. Assertions(断言)
在单元测试中是少不了断言的,Swift中的断言和Objective-C的区别不是太大,使用方法也是大同小异。下方就是断言的两种方法,由代码提示可知,在断言中的提示条件是可选的。断言会在Debug模式下起作用,但是在Release版本中就会被忽略。
在assert()函数中, 第一个参数是Bool类型,第二个参数是输出的信息。当条件为true时,断言不执行,相应的断言信息不打印。当条件为false时,断言执行,并且打印相应的断言信息。
assertionFailure()函数只有一个Message参数,并且该参数也是可以省略的,assertionFailure()没有条件。如下所示:
3. 先决条件(Preconditions)
Preconditions的用法和断言一样,不过有一点需要主要,Preconditions在debug和release模式下都会被执行,除非使用–Ounchecked进行编译。下方截图是代码提示给出的Preconditions函数的提示,如下所示:
关于Preconditions的具体用法请参照断言,和断言用法一样,在此就不做过多的赘述了。
二.Swift中的错误处理
在Objective-C中,如果你处理过错误的话,那么你将会对NSError很熟悉。在Swift中,如果你要定义你自己的错误类型,你只需要实现ErrorType协议即可。声明完错误类型后,就可以在处理错误抛出异常时使用自定义的错误类型了。下方将会一步步带你走完Swift中的错误处理的路程。
1.使用枚举创建错误类型
(1).遵循ErrorType协议,自定义错误类型。下方定义了一个错误类型枚举,该枚举遵循了ErrorType协议,在接下来的代码中我们将会使用这个MyCustomErrorType枚举,错误枚举的实现如下所示:
//定义错误类型 enum MyCustomErrorType: ErrorType { case ErrorReason case ErrorReason case ErrorReason }
</div>
(2).在我们的函数定义时可以使用throws关键字,以及在函数中使用throw关键字对错误进行抛出,抛出的错误类型就可以使用上面我们自己定义的错误类型。下方函数就是一个可以抛出错误的函数,抛出的错误就是我们在上面枚举中所定义的类型。具体代码如下所示:
func myThrowFunc() throws { let test:Int? = nil guard test != nil else { throw MyCustomErrorType.ErrorReason } }</div>
(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常量中存储的就是错误原因,具体实现方式如下:
struct MyErrorType: ErrorType { let reason : String }</div>
(2)上面定义完错误类型结构体后,在错误抛出中就可以使用了。在错误抛出时,可以传入一个错误原因,具体代码如下所示:
func myThrowFunc() throws { let test:Int? = nil guard test != nil else { throw MyErrorType(reason: "我是详细的错误原因,存储在error中") } }</div>
(3)最后要对抛出的错误进行do-catch处理,在处理时,可以对错误原因进行打印,错误原因存储在error中,具体操作和打印结果如下所示:
由上面的输出结果可知,error是我们自定义的MyErrorType类型,我们可以使用下面的代码来代替catch中的print语句,如下所示:
上面的做法似乎有些麻烦,还有一种简化输出的方法,就是在上述结构体中实现CustomDebugStringConvertible协议,对描述信息进行一个重写,就可以在打印error时,只打印错误信息,下方是重写后的结构体。
struct MyErrorType: ErrorType,CustomDebugStringConvertible { let reason : String var debugDescription: String { return "错误类型-----\(self.dynamicType): \(reason)" } }</div>
修改后,输出结果如下,直接打印error输出的就是错误信息,而不是MyErrorType类型。
3.使String类型遵循ErrorType协议,直接使用String提供错误原因
在“2”中,我们使用了结构体遵循ErrorType协议的形式,来为错误提供错误信息的。在接下来的部分,我们将通过更为简单的方式为抛出的错误提供错误信息。这种方式更为简单,也易于理解,具体方式如下方代码所示:
三、在错误处理中使用内置关键字
1.初探这些内置关键字
在Swift中提供了一些内置关键字(__FILE__, __FUNCTION__, __LINE__等)来获取上下文信息,在本篇博客的第三部分,将会给出如何在我们的错误处理中使用这些内置关键字。下方就是这些内置关键字的作用,如下所示: