Swift Day12 错误处理 泛型
2020-12-12 本文已影响0人
望穿秋水小作坊
一、错误(异常)
1. Swift 我们定义异常需要怎么做?
- Swift 中可以通过
Error协议自定义运行时的错误信息
enum SomeError : Error {
case illegalArg(String)
case outOfBounds(Int, Int)
case outOfMemory
}
2. 函数内部如何抛出错误?函数声明要加上什么?
- 函数内部通过
throw抛出自定义Error - 可能会抛出
Error的函数必须加上throws声明
func divide(_ num1: Int, _ num2: Int) throws -> Int {
if num2 == 0 {
throw SomeError.illegalArg("0 不能做为除数")
}
return num1 / num2
}
3. 如何调用一个有错误抛出的函数?
- 使用
try、try?、try!关键字
var result1 = try divide(100, 0);
var result2 = try? divide(100, 0);
var result3 = try! divide(100, 0);
4. 如何捕捉错误?错误爆出,会影响后续代码的执行吗?
- 使用
do-catch捕捉错误 - 抛出
Error后,try下一句直到作用域结束的代码都将停止运行
func test() {
print("1")
do {
print("2")
print(try divide(100, 0))
print("3")
} catch let SomeError.illegalArg(msg) {
print("参数异常:", msg)
} catch let SomeError.outOfBounds(size, index) {
print("下标越界:", size, index)
} catch SomeError.outOfMemory {
print("内存溢出")
} catch {
print("未知错误")
}
print("4")
}
- 上述代码将打印
1 2 参数异常:0不能作为除数 4
5. try?、try!处理错误的时候和 try 有什么不同?
- 可以使用
try?、try!调用可能会抛出Error的函数,这样就不需要去处理Error
func test2() {
print("1")
var result1 = try? divide(20, 10) // Optional(2), Int?
var result2 = try? divide(20, 0) // nil, Int?
var result3 = try! divide(20, 10) // 2, Int
}
6. defer 关键字有什么用?举一个实际例子说明?
-
defer语句:用来定义以任何方式(抛错误、return 等)离开代码块前必须要执行的代码 -
defer语句将延迟至当前作用域结束之前执行
defer
二、泛型(Generics)
1. 泛型(Generics)这个技术出现的目的是什么?
- 泛型可以将
类型参数化,提高代码复用率,减少代码量
泛型
2. 思考下面代码,红框内的 <T> 可以省略掉吗?
示例
- 不能省略掉,省略之后就是
普通函数了,编译器会认为 T 是一个自定义类型,如果找不到 T 的定义,就会编译报错
3. 思考为什么下列代码中,不需要像声明函数一样,加上 T 的类型说明?
示例
- 因为调用函数的时候当
i1传入的时候,编译器就可以确认T的类型,所以无需额外说明 - 总结:
就是要有办法让编译器在调用之时能确定 T 的类型
4. 我们声明 func 中泛型的时候一般用什么字母表示,我们声明 class 中的泛型的时候一般用什么字母表示,分别代表什么?
-
func中泛型的时候一般用T,表示 Type -
class中泛型的时候一般用E,表示 Element
class 中使用泛型
5. 泛型在 Class、Enum、Struct 中(了解)
示例
6.思考泛型的本质是什么呢?或者说实现原理?
- 在 C++中,编译器会根据代码需要,给泛型生成多个函数
C++的做法
- 当需要 Int 类型的泛型函数时,会生成
注释 1 的函数 - 当需要 Double 类型的泛型函数时,会生成
注释 2 的函数
- 那么在 Swift 中采取的做法是否和 C++一样呢?
Int 的 swapValues 函数调用地址
Double 的swapValues 函数调用地址
- 对比发现,两次调用的函数地址都一样,说明 Swift 采取的做法和 C++是不同的。
Swift 的做法
- 可以发现,Swift 将参数的
type metadata当成函数传入了callq中 - Swift 是将
type metadata作为函数的依据,来区别对待传入的泛型
7. 如果想在协议中使用泛型怎么做?
- 我们之前学过的泛型都是用在
类、结构体、枚举、函数中 - 在协议中想要使用泛型,需要使用
关联类型(associated type)技术
关联类型
8. 如果想对泛型进行约束要怎么做?(了解即可)
泛型约束