在swift2.0, 苹果在swift中就介绍了throws 关键字。这方便开发者在写代码时候保证清晰的思路以及在执行时可能出现的错误做异常处理。可做异常捕捉或者忽略异常。这篇文章将详细讲解 throws 关键字,以及在代码中如何去处理异常。

处理异常

如果你在Swift中使用过JSONDecoder,就知道在编写代码时候可抛出异常。让我们来看以下代码片段:

1
2
3
4
5
6
7
8
9
var greeting = "Hello, playground"

do {
let data = greeting.data(using: .utf8)!
let decoder = JSONDecoder()
let string = try decoder.decode(String.self, from: data)
} catch { error
print(error)
}

在Playground 执行代码片段,发现控制台输出Swift.DecodingError异常错误,异常被catch捕捉到了。代码中,我们用do {} catch {} 方式把可能发生的代码一次用try 方式写在do{}里面。当捕捉到一次立马进入catch{error }段。catch将捕捉到Error, 在catch时候,我们也可以捕捉精确的异常进行处理。

1
2
3
4
5
6
7
8
9
10
11
var greeting = "Hello, playground"

do {
let data = greeting.data(using: .utf8)!
let decoder = JSONDecoder()
let string = try decoder.decode(String.self, from: data)
} catch is DecodingError {
print("something went wrong while decoding!")
} catch { error
print(error)
}

Error在Swift中是一个协议,我们也可以定义自己错误类型,在一些业务错误中也可以使用到。

1
2
3
4
5
6
7
enum MyError: Error {
case myErrorCase
}

func doSomething() throws {
throw MyError.myErrorCase
}

当执行doSomething方法时候,程序会中端。try doSomething 你如果要捕捉则用do{} catch{}方式。如果你要忽略此异常,可以使用try? doSomething(),这个时候返回nil。如果你对返回值为nil不care的话,可以这么用。比如上面提到的json解析:
1
2
3
4
5
var greeting = "Hello, playground"
let data = greeting.data(using: .utf8)!
let decoder = JSONDecoder()
let string = try? decoder.decode(String.self, from: data)
print(string) //输出nil,当string变量被其他业务继续使用的时候,还是要做好nil值判断。

当你真的不在乎捕捉异常,或者不管错误是不是发生时候,你可以使用try?。 还有一种方式处理错误,你可以用try! 。但这种方式只适用于你非常肯定且有把握你的代码条件正确比如上述greeting变量一定要是复合json格式的字符串解析才能成功,否则非常危险,程序直接crash掉。

函数可以抛出异常方法必须含有throws关键字,如果有返回类型,则throw放在返回类型之前。

1
2
3
4
5
6
func doSomething() throws {
throw MyError.myErrorCase
}
func doSomething2() throws -> String? {
throw MyError.myErrorCase
}

当你不想在某方法里处理异常时候,你需要在使用的方法名后用throws将异常抛出到上层函数做处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func decodeJSON(_ data: Data) throws -> String {
let decoder = JSONDecoder()
let decodedString = try decoder.decode(String.self, from: data)

return decodedString
}

var greeting = "Hello, playground"
let data = greeting.data(using: .utf8)!
do {
let string = try decodeJSON(data)
print(string)
} catch {
print("decode error")
}

在block中抛异常

1
2
3
func executeGYBlock(_ closure: (() throws -> Void)) throws {
try closure()
}

这里出现了两个throws, executeGYBlock里的closure闭包可能有异常需要throws抛出,executeGYBlock 函数本身throws是在执行executeGYBlock函数时把内部block块可能出现的移除需要再次向上层调用者进行抛出异常。调用时如下代码示例:将block块内处理的错误进行抛出。

1
2
3
4
5
6
7
8
do {
try executeGYBlock {
let string = try decodeJSON(data)
print(string)
}
}catch{
print(error)
}

当有多个异常时候:catch首先捕捉到第一个try 的异常错误然后就中止。
1
2
3
4
5
6
7
8
9
10
11
do {
try executeGYBlock {
try doSomething()
}
try executeGYBlock {
let string = try decodeJSON(data)
print(string)
}
}catch{
print(error)
}

因为异常一旦catch后,程序里的代码片段将不再继续往下执行其他代码。用下面的代码表示
1
2
3
4
5
func doSomething() throws {
print("doSomethingBefore...")
throw MyError.myErrorCase
print("doSomethingAfter...") //Code after 'throw' will never be executed
}

总结

  1. 异常错误,我们可以用do{ try something} catch{}进行捕捉处理。
  2. Error是协议,我们可以自定义我们自己的异常类型。
  3. 异常可用try? 进行忽略,注意返回值nil的判断处理。
  4. 当你非常肯定不会有异常时候又不想写do catch 的时候,可以用try!。 但这种方式非常危险,万一有异常就直接crash。
  5. 当你方法里不想处理异常时候,在方法名处继续用throws 将异常抛出至上层。
  6. 当block块需要throws时,闭包函数需再次throws。最终把异常抛给调用处理。

评论