Swift Error只是个协议,不能直接使用传地址的方式传递协议Error
,
他可以通过as
与NSError
互相转化。
public protocol Error {
}
extension Error {
}
extension Error where Self : RawRepresentable, Self.RawValue : FixedWidthInteger {
}
可以发现什么都没有暴露出来,但它其实有我们最常用的属性localizedDescription
。
do {
try functionWhichWillThrow...
// success ↓↓
Statement2...
Statement3...
...
} catch {
// failure ↓↓
print(error.localizedDescription)
}
try..catch 语法和do
配合使用,当try后面的语句执行成功后会继续执行do
代码块后面的语句,如果失败了会执行catch
中的代码块,该代码块中会携带一个名为error
的参数。你也可以重命名该参数,如下例:
do {
try fucntion...
} catch let err {
print(err.localizedDescription)
}
1. Throw
Swift的错误一般通过throw
抛出异常,这样使得代码更加紧凑。
1.1 do...catch
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
print(json)
} catch {
print(error)
}
在OC中一般使用返回值或传入Error地址将错误返回,但Swfit的Error只是协议,故只有遵循该协议才能使用原OC的传递方式。
注意:OC Api在Swift中大部分都变更为了throw
的方式,但仍有部分保留该用法,例如C Api,你仍可以这么做。例如这样:
func getJson(data: Data, error: inout Error?) {
do {
try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
} catch let err {
error = err
}
}
var error: Error?
getJson(data: Data(), error: &error)
print(error)
通过inout
对传入的error进行赋值。
1.2 函数抛出异常
如果你需要抛出异常,那么你需要在函数返回值之前添加throws
关键字。
func getJson(data: Data) throws -> Any {
return try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
}
对于有异常可抛出的函数来说,可以直接传递其异常,也可以手动创建异常,如下:
func getJson(data: Data) throws -> Any {
if data.count == 0 {
throw NSError.init(domain: "", code: 0, userInfo: nil)
// throw NSError.init(domain: "", code: 0, userInfo: nil) as Error
}
return data
}
1.3 重写throw
函数
throw
函数的重写和普通函数基本一样。
class Paper {
func getJson(data: Data) throws -> Any {
if data.count == 0 {
throw NSError.init(domain: "", code: 0, userInfo: nil)
}
return data
}
}
class Book: Paper {
override func getJson(data: Data) throws -> Any {
return try super.getJson(data: data)
}
}
配合闭包返回值,函数返回值等,你可以有各种各样的
throw
用法。
2. Try
try
和有异常需要抛出的函数搭配使用,用法和as
相同:
try | 一般和do...catch 配合使用,关键字后面紧跟可能会抛出异常的函数,如果成功则继续支撑do 代码块后面的语句,失败则执行catch 代码块。 |
---|---|
try? | 可以在任何函数的上下文中使用,没有失败情况,要么成功,要么返回nil ,即失败返回nil ,一般用于不关心错误的情况。 |
try! | 如果确保try 函数一定会返回期望值,添加! 用于强制解包,和as! 同理。 |
let p = Paper()
let a = try? p.getJson(data: Data()) // Any?
let b = try! p.getJson(data: Data()) // Any
let c = a as! Any // Any
如果传入的data是有效值,那么a、b、c是完全相同的,try! = try? + as!
,否则使用!
会crash。
3. Error
由于Swift的Error是一个协议,我们并不能直接创建Error。
上例中也只是通过NSError
利用as
桥接为Error
类型,当我们需要使用的时候一般需要创建一个类型去实现Error协议。
enum MyError: Error {
case network
case jsonSerialization
case server
}
do {
let _ = try getJson(data: data)
} catch let error as MyError {
switch error {
case .jsonSerialization:
print("json 序列化失败")
case .network:
print("网络错误")
case .server:
print("服务器错误")
default:
print("未知错误")
}
} catch {
}
catch
和elseif
分支一样,可以添加多个,用于判断error类型或者重命名error。
localizedDescription
默认是The operation couldn’t be completed.
,故我们在这里重新定义该属性的内容。
enum MyError: Error {
case network
case jsonSerialization
case server
case unkown
var localizedDescription: String {
switch self {
case .jsonSerialization:
return "json 序列化失败"
case .network:
return "网络错误"
case .server:
return "服务器错误"
default:
return "未知错误"
}
}
}
Error不限于enum
,struct
,class
,并且可以根据自己的需要添加任意属性,比NSError灵活许多。
4. fatalError
fatalError
函数用于抛出异常,并且携带一段信息,该函数调用后一般是crash。
可用于截断当前上下文的编译器限制,例如一定要有返回值的场景,添加fatalError
及时后面没有返回值,也能正常编译。
func eat(food: String) -> String {
if food == "Apple" {
return "eat an apple"
} else if food == "Banana" {
return "eat a banana"
}
fatalError("不许吃其他水果")
}
eat(food: "Apple") // "eat an apple"
eat(food: "Egg") // Thread 1: Fatal error: 不许吃其他水果
当我们预料到某些情况一定会发生错误的时候,但实际使用上一定不会那么使用时,可以通过该方式防止编译器对语法进行检查,以免我们去添加一些无效代码去满足语法。
或者我们不允许某些我们不想看到的使用场景出现,则可以使用该函数,例如:
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
这是系统的默认实现,我们必须在这里进行初始化,或者返回nil
,如果不返回,要么这里需要将所有属性初始化完毕,要么fatalError
直接抛出异常。而这里则选择的是fatalError
,其实return nil
也可以避免初始化。
class View: UIView {
override class var layerClass: AnyClass {
return AVCaptureVideoPreviewLayer.self
}
var previewLayer: AVCaptureVideoPreviewLayer {
if let layer = self.layer as? AVCaptureVideoPreviewLayer {
return layer
} else {
fatalError("error")
}
}
}
这个例子layer一定是AVCaptureVideoPreviewLayer
类型,即if let
语法一定成立,fatalError
被用来打断编译器检查,其实直接使用as!
也一样。