错误、泛型

错误/异常

  • 开发过程常见的错误
    • 语法错误(编译报错)
    • 逻辑错误
    • 运行时错误(可能会导致闪退,一般也叫做异常)
    • ...

自定义错误

  • 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()
    }
}

上一篇:JUC集合: CopyOnWriteArrayList详解


下一篇:跨平台、跨语言应用开发,Elements 介绍