swift4.1 系统学习二十三 错误处理

/*
错误处理 (try - catch)

这是swift学习中的第十八章了。坚持仔细的阅读每一行文字也是不容易的。还有一百多页就看完了,一起加油哈。

对于大部分的现代面向对象的编程语言都拥有结构化的“错误处理”语法特性,swift也不例外。

*/

do {
    
    // 打开本地文件,并读取内容
    let str = try String.init(contentsOfFile: "/Users/ios/Desktop/study/swift/源码/swift23(错误处理)/text.txt")
    print("内容是 :\n \(str)")
} catch {
    print(" 文件读取错误 ")
}

// 如果把文件text.txt删掉,则会直接走到 catch中的内容。

// 1. swift中错误的表示
/*
在swift中如果我们要定义一个表示错误的类型非常简单,只需要遵循Error协议即可。我们通常用枚举或者
结构体类型来表示错误类型,枚举可能用的更多一些,因为它能够更加详细的表示出当前错误的细节。
*/

do {

print("\n---------1. swift中错误的表示-----------\n")

enum MyEnumError: Error {
    case errorOne
    case errorTwo
    
    // 实现Error协议的只读属性
    var localizedDescription: String {
        
        let desc = self == .errorOne ? "错误一" : "错误二"
        return "\(self): \(desc)"
    }
}

struct MyStructError: Error {
    
    var errorCode = 0
    
    var localizedDescription: String {
        return "错误码是 \(errorCode)"
    }
}

print("枚举的错误是: \(MyEnumError.errorOne.localizedDescription)")
print("结构体的错误是: \(MyStructError().localizedDescription)")

}

// 2.swift中的错误抛出
/*
本节详细描述如何在函数或方法中抛出错误对象。
如果我们要在函数或者方法中可能抛出异常,那么我们在该函数或者方法的形参列表的后面,加上 throws 关键字。
一旦我们抛出了错误,那么当前函数的后续代码就不会再继续执行了,也可以任务函数返回了。
*/

do {

print("\n-------2.swift中的错误抛出--------\n")

enum MyEnumError: Error {
    case errorOne
    case errorTwo
    case errorThree
    
    // 实现Error协议的只读属性
    var localizedDescription: String {
        
        var desc = ""
        switch self {
        case .errorOne:
            desc = "错误一"
        case .errorTwo:
            desc = "错误二"
        case .errorThree:
            desc = "错误三"
        }
        
        return "\(self): \(desc)"
    }
}

// 定义一个foo函数
func foo(a: Int) throws -> Int{
    if a < -10 {
        throw MyEnumError.errorOne
    } else if a > 10 {
        throw  MyEnumError.errorTwo
    } else if a == 0 {
        throw MyEnumError.errorThree
    }
    
    print("a = \(a)")
    return a
}

let ref = foo(a:)
let r = try ref(5)
print("r = \(r)")

func test(a: Int) throws {
    
    print("这是一个测试 a = \(a)")
    let value = try foo(a: a)
    
    print("value = \(value)")
}

do {
    try test(a: 100)
} catch {
    if let error = error as? MyEnumError {
        print("抛出异常了, \(error.localizedDescription)")
    }
}

/*
 打印错误:
 a = 5
 r = 5
 这是一个测试 a = 100
 抛出异常了, errorTwo: 错误二
 */

}

// 3. 错误捕获与处理
/*
swift中,我们使用 do-catch语句块对错误进行捕获。当我们在调用一个throws关键字声明的函数或方法
的时候,我们必须把此调用语句放在do语句块中,同时在do语句块之后紧接着使用catch语句块。如果do语句
块中抛出异常,则直接进入catch语句块中。如果没有异常,catch语句块不会被执行。
*/

do {

print("\n-----------3. 错误捕获与处理--------------\n")

enum ErrorType: Error {
    case errorOne
    case errorTwo
    case errorThree
    
    var localizedDescription: String {
        
        switch self {
        case .errorOne:
            print("抛出错误一")
        case .errorTwo:
            print("抛出错误二")
        case .errorThree:
            print("抛出错误三")
        }
        return "\(self)"
    }
}

func test(b: String) throws {
    
    if b.count == 0 {
        throw ErrorType.errorOne
    } else if b.count > 10 {
        throw ErrorType.errorThree
    }
    
    print("b = \(b)")
}

// 调用
do {
    
    print("调用一")
    try test(b: "")
    print("调用二")
    try test(b: "abc")
    print("调用三")
    try test(b: "aaaaabbbbbsssssdddf")
    
} catch {
    
    if let myerror = error as? ErrorType {
        print("myerror = \(myerror.localizedDescription)")
    }
}

/*
 结果:
 调用一
 抛出错误一
 myerror = errorOne
 */

// 从结果中可以看出,try test(b: "") 抛出异常之后,下面的语句没有执行,直接跑出了异常。

/*
 swift的catch语句也是很丰富的,跟case语句有点儿类似,后面可以跟各种不同的模式,而且多条
 catch语句去匹配多种不同的错误对象。此外,catch后面的表达式也可以缺省,一旦缺省,那么该
 catch语句块将可接收所有错误类型的对象。
 */

do {
    
    print("\n")
     try test(b: "")
     try test(b: "aaaaa")
     try test(b: "hhhhhhhhhhhhhhhhhhhhhh")
} catch ErrorType.errorOne {
    print("错误一发生")
} catch let err as ErrorType {
    print("错误是 \(err.localizedDescription)")
} catch {
    print("错误发生")
}

}

/*
注意:
如果do语句块中抛出的异常没有任何一个catch语句块捕获到,那么这个错误就会抛给系统,引发崩溃!!!
*/

do {

enum MyError: Error {
    case tooSmall(Double)
    case tooLarge(Double)
    case zero
}

func foo(value: Double) throws {
    
    if value < -100.0 {
        throw MyError.tooSmall(value)
    } else if value > 100.0 {
        throw MyError.tooLarge(value)
    } else if value == 0 {
        throw MyError.zero
    }
    
    print("正常数据 \(value)")
    
}

do {
    
    try foo(value: 0.0)
    try foo(value: -200.0)
    try foo(value: 200.0)
} catch MyError.tooSmall(let value) {
    print("the value \(value) is too small")
} catch MyError.tooLarge(let value) {
    print("the value \(value) is too large")
} catch MyError.zero {
    print("the value  is zero")
}

print("完成")

// 上面的 catch语句少了任何一项都会直接崩溃,因此我们在捕获异常的时候一定要把情况写全。

}

// 4. rethrows修饰的函数与方法
/*
如果我们定义了一个函数或者方法,它含有一个可能会抛出错误的函数引用参数,并且对当前函数,只有当
这个函数引用参数抛出错误时它才会把这个错误向上抛出,那么我们可以用rethrows来修饰这个函数。
*/
do {

print("\n-------------------\n")

enum MyError: Error {
    case tooSmall(Double)
    case tooLarge(Double)
    case zero
}

/// 定义一个foo函数,
/// 它可能会抛出一个错误,
/// 因此这里用throws限定
func foo(value: Double) throws {
    if value < -100.0 {
        throw MyError.tooLarge(value)
    }
    else if value > 100.0 {
        throw MyError.tooLarge(value)
    }
    else if value == 0.0 {
        throw MyError.zero
    }
    
    print("value = \(value)")
}

// 定义一个rethrows修饰的test函数
func test(a: Double, fun: (Double) throws -> Void) rethrows {
    try fun(a)
}

// 重载test函数
func test(value: Double, fun: (Double) throws -> Void) rethrows {
    
    do {
        try fun(value)
    } catch {
        throw MyError.zero
    }
}

// 调用
do {
    
    try test(a: 100.0, fun: foo(value:))
    try test(a: 0.0, fun: foo(value: ))
} catch let err as MyError {
    
   print("error is \(err)")
}

}

protocol Prot {
    
    func method(fun: (Int) throws -> Void) rethrows
    
    func method(ref: (Int) throws -> Void) throws
}
struct MyStruct: Prot {
    
    // 实现协议中的方法
    // 这里只能使用rethrows
    func method(fun: (Int) throws -> Void) rethrows {
        
        print("哈哈哈哈哈")
    }
    
    // 这里可以使用throws也可以使用rethrows
    func method(ref: (Int) throws -> Void) throws {
        
        print("呵呵呵呵呵呵呵呵呵呵")
    }
    
}

// 5. 将错误转换为可选值
/*
swift编程语言引入了将错误转为可选值的语法机制,可使我们用if语句或者guard语句直接处理可能会抛出的
错误。在swift中,我们可以直接使用 try? 将可能抛出的错误的函数或方法调用转为可选值。
*/

do {

print("\n---------5. 将错误转换为可选值--------\n")

enum YourError: Error {
    case tooSmall
    case tooLarge
}

func foo(value: Int) throws {
    if value < -100 {
        throw YourError.tooSmall
    } else if value > 100 {
        throw YourError.tooLarge
    }
    
    print("value = \(value)")
}

func test(a: Int) throws -> Int {
    try foo(value: a)
    return a
}

// 直接通过try调用foo函数
let v1 = try? foo(value: -200)

// 我们看一下v1的值
print("v1 = \(String(describing: v1))")
//打印: v1 = nil

let v2 = try? foo(value: 10)
print("v2 = \(String(describing: v2))")
/*
 value = 10
 v2 = Optional(())
 */

if let _ = try? foo(value: 200) {
    print("OK")
}

guard let value = try? test(a: 20) else {
    exit(-1)
}

print("value = \(value)")

}

// 6. 指定清理行为 defer
/*
swift中defer语句块非常灵活,它作用于某个语句块作用域,而不是单单是函数作用域。
*/

do {

print("\n----------6. 指定清理行为 defer-----------\n")

enum CustomError: Error {
    case tooSmall
    case tooLarge
}

func foo(value: Int) throws {
    if value < -100 {
        throw CustomError.tooSmall
    } else if value > 100 {
        throw CustomError.tooLarge
    }
    print("value = \(value)")
}

func test(a: Int) {
    if a > 0 {
        defer {
            print("这是是defer,开始执行了")
        }
    }
    if a > 100 {
        print("即将执行return语句")
        return
    }
    print("a = \(a)")
    
    defer {
        print("我哈哈哈哈,这又是一个defer")
    }
    
    do {
        
        try foo(value: a)
    } catch let err {
        print("error \(err) 发生啦")
        return
    }
    
    print("测试结束")
}

// 调用
test(a: 10)
print("\n")

test(a: 200)
print("\n")

test(a: -200)
print("\n")

test(a: -10)
print("\n")

/*
 结果:
 
 这是是defer,开始执行了
 a = 10
 value = 10
 测试结束
 我哈哈哈哈,这又是一个defer
 
 
 这是是defer,开始执行了
 即将执行return语句
 
 
 a = -200
 error tooSmall 发生啦
 我哈哈哈哈,这又是一个defer
 
 
 a = -10
 value = -10
 测试结束
 我哈哈哈哈,这又是一个defer
 */

}

/*
如果同一个作用域出现多个defer语句,那么它们是如何执行的呢?
*/

do {

func test() {
    
    defer {
        print("第一个defer")
    }
    
    print("第一段")
    
    defer {
        print("第二个defer")
    }
    print("第二段")
    
    defer {
        print("第三个defer")
    }
    print("第三段")
}

test()

/*
 结果:
 
 第一段
 第二段
 第三段
 第三个defer
 第二个defer
 第一个defer
 */

}

// 7.Never修饰的函数
/*
当一个函数早执行后不会返回时,我们用Never作为该函数的返回类型,表示该函数不能返回。
我们什么时候会用到不能返回的函数呢?
如果我们程序因为某些状态而导致只能做崩溃或退出处理,那么我们就可以调用一个不能返回的函数来抛出错误或
直接退出。
在一个返回类型为Never的函数中,它不应该存在任何一个分支或条件使得该函数能够自然返回。
*/

do {
    
    enum HerError: Error {
        case positive, negative
    }
    
    func foo(a: Int) throws -> Never {
        throw a >= 0 ? HerError.positive : HerError.negative
    }
    
    func foo() -> Never {
        exit(-1)
    }
    
    func infinity() -> Never {
        
        while true {
            
        }
    }
    
//    func invalidNever() -> Never {
//        // 在此函数中,由于没有任何实现,该函数将直接返回,从而引发编译错误
//        // 报错:Function with uninhabited return type 'Never' is missing call to another never-returning function on all paths
//    }
    
}

你可能感兴趣的:(swift4.1 系统学习二十三 错误处理)