在Swift 2.0版本中,Swift语言对其错误处理进行了新的设计,固然了,从新设计后的结果使得该错误处理系统用起来更爽。今天博客的主题就是系统的搞一下Swift中的错误处理,以及看一下Swift中是如何抛出异常的。在编译型语言中,错误通常分为编译错误和运行时错误。咱们平时在代码中处理的错误为运行时错误,咱们对异常进行处理的操做的目的是为了防止程序出现错误而致使其余的反作用,好比用户数据未保存等等。html
在今天的博客中,先给出主动产生异常的几种状况,而后再给出如何处理被动异常。数组
1、主动退出程序的几种状况函数
在Objective-C中,在单元测试时咱们会使用断言,断言中条件知足时会产生异常,并打印出相应的断言错误,在Swift中也有几种产生异常的语法。在本篇博客的第一部分就给出这几种方法。post
1.Fatal Errors(致命的错误)单元测试
使用fatalError()函数能够当即终止你的应用程序,在fatalError()中能够给出终止信息。使用fatalError()函数,会毫无条件的终止你的应用程序,用起来也是比较简单的,就是一个函数的调用。下方这个Demo一目了然呢,在此就不作过多赘述了。测试
2. Assertions(断言)url
在单元测试中是少不了断言的,Swift中的断言和Objective-C的区别不是太大,使用方法也是大同小异。下方就是断言的两种方法,由代码提示可知,在断言中的提示条件是可选的。断言会在Debug模式下起做用,可是在Release版本中就会被忽略。spa
在assert()函数中, 第一个参数是Bool类型,第二个参数是输出的信息。当条件为true时,断言不执行,相应的断言信息不打印。当条件为false时,断言执行,而且打印相应的断言信息。debug
assertionFailure()函数只有一个Message参数,而且该参数也是能够省略的,assertionFailure()没有条件。以下所示:设计
3. 先决条件(Preconditions)
Preconditions的用法和断言同样,不过有一点须要主要,Preconditions在debug和release模式下都会被执行,除非使用–Ounchecked进行编译。下方截图是代码提示给出的Preconditions函数的提示,以下所示:
关于Preconditions的具体用法请参照断言,和断言用法同样,在此就不作过多的赘述了。
二.Swift中的错误处理
在Objective-C中,若是你处理过错误的话,那么你将会对NSError很熟悉。在Swift中,若是你要定义你本身的错误类型,你只须要实现ErrorType协议便可。声明完错误类型后,就能够在处理错误抛出异常时使用自定义的错误类型了。下方将会一步步带你走完Swift中的错误处理的路程。
1.使用枚举建立错误类型
(1).遵循ErrorType协议,自定义错误类型。下方定义了一个错误类型枚举,该枚举遵循了ErrorType协议,在接下来的代码中咱们将会使用这个MyCustomErrorType枚举,错误枚举的实现以下所示:
1 //定义错误类型 2 enum MyCustomErrorType: ErrorType { 3 case ErrorReason1 4 case ErrorReason2 5 case ErrorReason3 6 }
(2).在咱们的函数定义时可使用throws关键字,以及在函数中使用throw关键字对错误进行抛出,抛出的错误类型就可使用上面咱们本身定义的错误类型。下方函数就是一个能够抛出错误的函数,抛出的错误就是咱们在上面枚举中所定义的类型。具体代码以下所示:
1 func myThrowFunc1() throws { 2 3 let test:Int? = nil 4 5 guard test != nil else { 6 throw MyCustomErrorType.ErrorReason1 7 } 8 }
(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常量中存储的就是错误缘由,具体实现方式以下:
1 struct MyErrorType: ErrorType { 2 let reason : String 3 }
(2)上面定义完错误类型结构体后,在错误抛出中就可使用了。在错误抛出时,能够传入一个错误缘由,具体代码以下所示:
1 func myThrowFunc2() throws { 2 3 let test:Int? = nil 4 5 guard test != nil else { 6 throw MyErrorType(reason: "我是详细的错误缘由,存储在error中") 7 } 8 }
(3)最后要对抛出的错误进行do-catch处理,在处理时,能够对错误缘由进行打印,错误缘由存储在error中,具体操做和打印结果以下所示:
由上面的输出结果可知,error是咱们自定义的MyErrorType类型,咱们可使用下面的代码来代替catch中的print语句,以下所示:
上面的作法彷佛有些麻烦,还有一种简化输出的方法,就是在上述结构体中实现CustomDebugStringConvertible协议,对描述信息进行一个重写,就能够在打印error时,只打印错误信息,下方是重写后的结构体。
1 struct MyErrorType: ErrorType,CustomDebugStringConvertible { 2 let reason : String 3 var debugDescription: String { 4 return "错误类型-----\(self.dynamicType): \(reason)" 5 } 6 }
修改后,输出结果以下,直接打印error输出的就是错误信息,而不是MyErrorType类型。
3.使String类型遵循ErrorType协议,直接使用String提供错误缘由
在“2”中,咱们使用告终构体遵循ErrorType协议的形式,来为错误提供错误信息的。在接下来的部分,咱们将经过更为简单的方式为抛出的错误提供错误信息。这种方式更为简单,也易于理解,具体方式以下方代码所示:
3、在错误处理中使用内置关键字
1.初探这些内置关键字
在Swift中提供了一些内置关键字(__FILE__, __FUNCTION__, __LINE__等)来获取上下文信息,在本篇博客的第三部分,将会给出如何在咱们的错误处理中使用这些内置关键字。下方就是这些内置关键字的做用,以下所示:
上面说是内置关键字,其实就是存储代码上下文的宏定义,上方代码段简单的给出了这些内置关键字的做用与用法,在接下来将在ErrorType中使用这些内置关键字,让咱们的错误信息更加丰富多彩。
2.在ErrorType中使用上述内置关键字
若是想在ErrorType中使用这些上下文内置关键字,咱们只须要对ErrorType进行扩展,使其在ErrorType提供错误信息时给出出错的上下文信息。固然,这实现起来比较简单,就是在ErrorType中添加了一个扩展方法contextString()。该方法的做用就是提供错误的上下文信息,也就是在出错的地方,调用contextString()方法生成上下文描述信息便可。对ErrorType协议的具体延展实现以下代码段所示.
在下方代码片断中,咱们对ErrorType进行了扩展,为ErrorType添加了contextString的函数实现。contextString()函数有三个默认参数,分别是file--当前文件名,function--当前出错的函数名,line--当前抛出异常的行数。上述三个参数都有参数默认值,分别对应着__FILE__, __FUNCTION__, __LINE__。该扩展函数的返回值为这三个参数组成从字符串信息。具体实现以下所示:
3.使用扩展的contextString方法
上面咱们使用结构体实现ErrorType协议的形式,为错误类型添加错误缘由。接下来咱们将在添加reason的同时,使用contextString()函数添加描述信息。下方CustomErrorType结构体遵循了ErrorType协议,其中添加了一个reason常量来存储错误缘由,一个context常量来存储上下文信息,而且为该结构体添加了一个构造函数,在构造函数中初始化和reason常量。具体实现以下所示:
4. 抛出并捕获异常
在下方代码中函数throwError()抛出了异常,该抛出的错误类型是CustomErrorType。在建立CustomErrorType类型实例,也就是err变量时,咱们指定了错误缘由,也就是为reason赋了一个值。在建立完err实例后,咱们又调用延展contextString()函数获取异常的上下文信息,并把返回的内容存储在err实例的context属性中。最后使用throw关键字抛出err实例,以下方第一部分代码所示。
在建立抛出异常的函数后,咱们须要对抛出的异常进行捕获。也就是使用try对异常进行捕获,使用do-catch对异常进行处理,具体操做以下方第二段代码所示。
5. 分析打印结果
通过上述步骤若是你在Playground中进行试验的,那么在控制台上你将会看到以下信息。从打印出的信息咱们能够看到,信息包括reason:错误缘由,和context:异常上下文。在下方的输出结果中,文件名咱们能够看到是<EXPR>这并非确切的文件名,由于咱们是在Playground中使用的,而且不是确切的Swift源文件,因此获取不到确切的文件名。
为了观察确切的文件名,咱们须要在确切的Swift源文件中抛出上述异常。在特定Swift源文件中,咱们会看到下方的输出结果。从下方的输出日志中,咱们能够清楚的看到文件名是一个详细的文件路径。以下所示:
今天的博客内容也够多的了,就先到这儿吧,之后在作小的Demo时,若是用到其余的错误处理方式,在作详细介绍呢。