Error 异常处理 错误处理
错误类型
1.语法错误(编译报错)
2.逻辑错误
3.运行时错误 (可能会导致闪退 一般也叫做异常)
自定义错误
可以通过Error协议自定义运行时的错误信息
enum SomeError: Error {
case illegalArg(String)
case outOfBounds(Int, Int)
case outOfMemory
}
函数内部通过throw抛出自定义Error 可能会抛出Error的函数必须加上throws声明
struct MyError: Error {
var msg: String
}
func divide(_ num1: Int, _ num2: Int) throws -> Int {
if num2 == 0 {
throw MyError(msg: "0不能作为除数")
}
return num1 / num2
}
需要使用try调用可能会抛出Error的函数
var result = try divide(2, 0)
print(result)
如何处理错误
do-catch
一旦 try 抛出异常 它所在作用域范围内的后面的所有代码都不会执行
func test() {
print("1")
do {
print("2")
print(try divide(20, 0))
print("3")
}catch let SomeError.illegalArg(msg){
print("参数异常:", msg)
}catch let SomeError.outOfBounds(size, index){
print("下标越界:", "size = \(size)", "index = \(index)")
}catch SomeError.outOfMemory{
print("内存溢出")
}catch {
print("其他错误")
}
print("4")
}
test()
1
2
参数异常: 0不能作为除数
4
不捕捉Error 在当前函数增加throws声明 Error将自动抛给上层函数
- 如果最顶层函数(main 函数) 依然没有捕捉Error 那么程序将终止
func test() throws{
print(try divide(20, 0))
}
try test()
func test() throws{
print("1")
do {
print("2")
print(try divide(20, 0))
print("3")
}catch let error as SomeError{
print(error)
}
print("4")
}
try test()
1
2
illegalArg("0不能作为除数")
4
异常一层一层抛出
func test0 throws {
try test1()
}
func test1 throws {
try test2()
}
func test2 throws {
do{
print(try divide(288, 81))
}catch is SomeError {
priht("This is SomeError")
}
}
try test0()
try? try!
可以使用try? try! 调用可能会抛出Error的函数 这样就不用去处理Error
func test() {
print("1")
var result1 = try? divide(20, 10)
var result2 = try? divide(20, 0)
var result3 = try! divide(20, 10)
print("2")
}
test()
a b 是等价
var a = try? divide(20, 0)
var b: Int?
do {
b = try divide(20, 0)
}catch{
b = nil
}
rethrows
rethrows 函数本身不会抛出错误 但是调用闭包函数抛出错误 那么他会将错误往上抛
rethrows 跟 throws 一样抛出异常 只不过 rethrows是由于函数参数是闭包函数导致的异常
func exec(_ fn: (Int, Int) throws -> Int, _ num1: Int, _ num2: Int) rethrows {
print(try fn(num1,num2))
}
try exec(divide, 29, 0)
defer
用来定义以任何方式(抛错误 return等) 离开代码前必须要执行的代码
defer语句将延迟至当前作用域结束之前执行
func open(_ filename: String) -> Int {
print("open")
return 0
}
func close(_ file: Int) {
print("close")
}
func processFile(_ filename: String) throws {
let file = open(filename)
defer {
close(file)
}
try divide(20, 0)
}
try processFile("test.text")
defer语句的执行顺序与定义顺序相反
泛型 Generics
泛型可以将类型参数化 提供代码复用率 减少代码量
T 代表一种不确定的类型
var n1 = 10
var n2 = 20
func swapValues<T>(_ a: inout T , _ b: inout T) {
return (a,b) = (b,a)
}
swap(&n1, &n2)
print(n1,n2)
函数类型 必须明确是什么类型
var fn: (inout Int, inout Int) -> () = swapValues
泛型类型
class Stack<E> {
var elements = [E]()
func push(_ element: E){
return elements.append(element)
}
func pop() -> E {
return elements.removeLast()
}
func top() -> E {
return elements.last!
}
func size() -> Int {
return elements.count
}
}
var stack = Stack<Int>()
stack.push(<#T##element: Int##Int#>)
class SubStack<E>: Stack<E> {
}
enum Score<T> {
case point(T)
case grade(String)
}
let score = Score<Int>.grade("A")
关联类型
1.关联类型的作用 给协议中用到的类型定义一个占位名称
2.协议中拥有多个关联类型
protocol Stackable {
associatedtype Element //关联类型
mutating func push(_ element: Element)
mutating func pop() -> Element
func top() -> Element
func size() -> Int
}
class StringSatck: Stackable {
//给关联类型设定真实类型
typealias Element = String
func push(_ element: Element){
return elements.append(element)
}
func pop() -> Element{
return elements.removeLast()
}
func top() -> Element {
return elements.last!
}
func size() -> Int {
return elements.count
}
}
类型约束
协议类型的注意点
如果协议中有associatedtype 会报错
不透明类型 是为了只开放类型里面的部分接口 (也就是协议里面定义的接口) 不暴露类型