错误/异常
- 开发过程常见的错误
- 语法错误(编译报错)
- 逻辑错误
- 运行时错误(可能会导致闪退,一般也叫做异常)
- ...
自定义错误
- Swift 中可以通过 Error 协议自定义运行时的错误信息
- 比如 Alamofire 中就定义了很多错误
enum MyError: Error {
case zero
case outOfRange(Int, Int)
}
func divide(_ num1: Int, _ num2: Int) -> Int {
return num1 / num2
}
func divide1(_ num1: Int, _ num2: Int) -> Int? {
guard num2 != 0 else {
return nil
}
return num1 / num2
}
func divide2(_ num1: Int, _ num2: Int) throws -> Int {
guard num2 != 0 else {
throw MyError.zero
}
return num1 / num2
}
print(divide(1, 2))
print(divide1(1, 0))
do {
print("1")
print(try divide2(1, 0))
print("2")
} catch MyError.zero {
print("zero")
} catch {
print("other")
}
//0
//nil
//1
//zero
抛出 Error 后,try 下一句直到作用域结束的代码都将停止运行
处理 Error
- 通过 do-catch 捕捉 Error
- 不捕捉 Error,在当前函数增加 throws 声明,Error将自动抛给上层函数
- 如果最顶层函数(main函数)依然没有捕捉Error,那么程序将终止
错误捕捉
- catch is SomeError
- catch let error as SomeError
try?、try!
- 可以使用 try?、try! 调用可能会抛出 Error 的函数,这样就不用去处理 Error
func test(_ i: Int) throws -> Int {
if i == 0 {
throw MyError.zero
}
return 1
}
print("begin")
print(try? test(0))
print(try? test(1))
print(try! test(1))
print("end")
// 和 try? test(0) 是等价的
var b: Int?
do {
b = try test(0)
} catch {
b = nil
}
------------------------执行结果------------------------
begin
nil
Optional(1)
1
end
rethrows
- rethrows 表明:函数本身不会抛出错误,但调用闭包参数抛出错误,那么他会将错误向上抛
func exec(_ fn: (Int, Int) throws -> Int, _ num1: Int, _ num2: Int) rethrows {
print(try fn(num1, num2))
}
//try exec(divide, 20, 10)
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T
defer
- 用来定义以任何方式离开代码块前必须要执行的代码
- 将延迟至当前作用域结束之前执行
- defer 语句的执行顺序和定义顺序相反
enum MyError: Error {
case zero
case outOfRange(Int, Int)
}
func open() {
print("open file")
}
func close() {
print("close file")
}
func test(_ i: Int) throws -> Int {
if i == 0 {
throw MyError.zero
}
return 1
}
open()
defer {
close()
}
try test(0)
泛型(Generics)
- 泛型可以将类型参数化,提高代码复用率,减少代码量
func swapValue<T>(_ a: inout T, _ b: inout T) {
(a, b) = (b, a)
}
var a = 10
var b = 20
swap(&a, &b)
var c = 10.0
var d = 20.0
swap(&c, &d)
-------------------------汇编分析------------------------
movq $0xa, -0x8(%rbp)
movq $0x14, -0x10(%rbp)
movq 0xd929(%rip), %rdx ; (void *)0x00007fff8afc1b58: type metadata for Swift.Int // 传入了元类信息
leaq -0x8(%rbp), %rdi
leaq -0x10(%rbp), %rsi
callq 0x10001182c ; symbol stub for: Swift.swap<A>(inout A, inout A) -> ()
movq 0xd8f1(%rip), %rdx ; (void *)0x00007fff8afc14e0: type metadata for Swift.Double //传入了元类信息
callq 0x10001182c ; symbol stub for: Swift.swap<A>(inout A, inout A) -> ()
// 函数地址一样 同一函数
// 类
class Stack<E> {
var elements = [E]()
func push(_ element: E) { elements.append(element) }
func pop() -> E { elements.removeLast() }
func top() -> E { elements.last! }
func size() -> Int { elements.count }
}
class subStack<E> : Stack<E> { }
var stack = Stack<Int>()
stack.push(11)
stack.push(12)
stack.push(13)
print(stack.top()) // 13
print(stack.pop()) // 13
print(stack.size()) // 2
// 结构体
struct Stack<E> {
var elements = [E]()
mutating func push(_ element: E) { elements.append(element) }
mutating func pop() -> E { elements.removeLast() }
func top() -> E { elements.last! }
func size() -> Int { elements.count }
}
关联类型(Associated Type)
- 关联类型的作用:给协议中用到的类型定义一个占位名称
- 协议中可以拥有多个关联类型
protocol Stackable {
associatedtype Element
mutating func push(_ element: Element)
mutating func pop() -> Element
func top() -> Element
func size() -> Int
}
class Stack<E>: Stackable {
// typealias Element = E
var elements = [E]()
func push(_ element: E) { elements.append(element) }
func pop() -> E { elements.removeLast() }
func top() -> E { elements.last! }
func size() -> Int { elements.count }
}
类型约束
就是可以约束泛型
protocol Runnable { }
class Person { }
func swapValues<T : Person & Runnable>(_ a: inout T, _ b: inout T) {
(a , b) = (b , a)
}
不透明类型
- 使用 some 的时候只允许返回一种类型,不允许返回两种类型
- 除了用在返回值类型上,一般还可以用在属性类型上
- 为了屏蔽的类内部的实现 原因:当 protocol 中有关联类型时,如果返回的是 protocol 那么他在编译的时候没有办法,确定是哪个类
protocol Runnable {
associatedtype Speed
var speed: Speed { get }
}
class Person: Runnable {
var speed: Int = 0
}
class Car: Runnable {
var speed: Double = 0.0
}
//报错:Protocol 'Runnable' can only be used as a generic constraint because it has Self or associated type requirements
func get(_ type: Int) -> Runnable {
if type == 0 {
return Person()
}
return Car()
}
//解决方案一:
func get1<T: Runnable>(_ type: Int) -> T {
if type == 0 {
return Person() as! T
}
return Car() as! T
}
//解决方案二:使用 some
func get2(_ type: Int) -> some Runnable {
return Car()
}
var r1 = get(0) // 只知道 r1 是 Runnable 类型
// 用于屏蔽内部细节
class Dog: Runnable {
var speed: Int = 10
}
class Person {
var pet: some Runnable {
return Dog()
}
}