一、错误类型
开发过程常见的错误
语法错误(编译报错)
逻辑错误
运行时错误(可能会导致闪退,一般也叫做异常)
2.1 通过结构体
第一步
struct MyError : Errort {
var msg: String
}
第二步
func divide(_ num1: Int, - num2: Int) throws -> Int ‹
if num2 == 0 {
第三步
throw MyError(msg:"0不能作为除数")
}
return numi / num2
}
divide (1,0)
2.2 枚举定义错误信息
Swift中可以通过Error协议自定义运行时的错误信息
enum SomeError : Error {
case illegalArg(String)
case outOfBounds(Int, Int) case outOfMemory
}
函数内部通过throw抛出自定义Error ,可能会抛出Error的函数必须加上throws声明
func divide(_ num1: Int, _ num2: Int) throws -> Int {
if num2 == 0 {
throw SomeError.illegalArg("0不能作为除数")
}
return num1 / num2
}
需要使用try调用可能会抛出Error的函数
var result = try divide(20, 10)
2.3 处理错误信息do-catch
可以使用do-catch捕捉Error
func test {
print ("1")
do {
print ("2")
print(try divide (200, 0))
//一旦这句回抛出异常,do作用越后边的代码都不会执行,也就是后边的都不会执行
print ("3")
print ("3")
print ("3")
print ("3" )
print ("3")
¿ catch let SomeError.illegalArg(msg) ‹
print ("参数异常:",msg)
} catch let SomeError.outOfBounds(size, index) {
print("下标越界:","size=\(size)",
, "index=\(index)")
} catch SomeError.outOfMemory 1
print("内存溢出")
¿ catch {
print("其他错误")
print ("4")
}test()
// 1
// 2
// 参数异常 : 0不能作为除数 // 4这种方式和上面的那种方式一样,先拿到所有的error 再casedo {
try divide(20, 0)
} catch let error {
switch error {
case let SomeError.illegalArg(msg):
print("参数错误: ", msg)default:
print("其他错误")
}
}
抛出Error后,try下一句直到作用域结束的代码都将停止运行
2.4 处理Error
处理Error的2种方式
① 通过do-catch捕捉Error
② 不捕捉Error ,在当前函数增加throws声明,Error将自动抛给上层函数
如果最顶层函数( main函数)依然没有捕捉Error ,那么程序将终止
这里抛给调用者处理
func test() throws {
print("1")
print(try divide(20, 0))
print("2")
}
try test()
// 1
// Fatal error: Error raised at top level处理方式一
do {
print(try divide(20, 0))
} catch is SomeError {
print("SomeError")
}处理方式二
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
2.5 注意点
func testo(){
test1 ()
}func test1() {
test2 ()
}
func test2() {
do {
print(try divide (200, 0)) 2
如果这里只写一个catch的情况还是回报错,告诉你没有处理所有的错误信息
} catch is SomeError {
print ("This is SomeError")
}解决办法
func test2) {
do {
print(try divide (200, 0))
} catch is SomeError {
print ("This is SomeError")
} catch {
}
}或者往上抛
func test0()throws {
try test1()
}
}
func test1() throws {
try test2()
}func test2() throws {
do {
print(try divide (200,0))
}catch is SomeError {
print ("This is SomeError")}
}
二、try?、 try!
可以使用try?、 try!调用可能会抛出Error的函数,这样就不用去处理Error
func test() {
print("1")
var result1 = try? divide(20, 10) // Optional(2), Int?
var result2 = try? divide(20, 0) // nil
var result3 = try! divide(20, 10) // 2, Int
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表明:函数本身不会抛出错误,但调用闭包参数抛出错误,那么它会将错误向上抛
func exec(_ fn: (Int, Int) throws -> Int, _ num1: Int, _ num2: Int) rethrows {
print(try fn(num1, num2))
}
// Fatal error: Error raised at top level
try exec(divide, 20, 0)
四、defer
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)
}
// 使用file // ....
try divide(20, 0)// close将会在这里调用
}
try processFile("test.txt")
// open
// close
// Fatal error: Error raised at top level
defer语句的执行顺序与定义顺序相反
func fn1() { print("fn1") }
func fn2() { print("fn2") }
func test() {
defer { fn1() }
defer { fn2() }
}
test()
// fn2
// fn1
二、泛型(Generics )
2.1 范型函数的定义和普通函数的定义的区别
swapValues<T> 这里要加<T> 表示范型函数
型可以将类型参数化,提高代码复用率,减少代码量
func swapValues<T>(_ a: inout T, _ b: inout T) {
(a, b) = (b, a)
}var i1 = 10 var i2 = 20
swapValues(&i1, &i2)var d1 = 10.0 var d2 = 20.0
swapValues(&d1, &d2)struct Date {
var year = 0, month = 0, day = 0
}
var dd1 = Date(year: 2011, month: 9, day: 10)
var dd2 = Date(year: 2012, month: 10, day: 11)
swapValues(&dd1, &dd2)
泛型函数赋值给变量
func test<T1, T2>(_ t1: T1, _ t2: T2) {}
var fn: (Int, Double) -> () = test
2.2 泛型类型
2.2.1 泛型类型类
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 }
}var stack = Stack<Int>()
stack.push(11)
stack.push(22)
stack.push(33)
print(stack.top()) // 33
print(stack.pop()) // 33
print(stack.pop()) // 22
print(stack.pop()) // 11
print(stack.size()) // 0
2.2.2 泛型类型 继承
class SubStack<E> : Stack<E> {}
2.2.3 泛型类型结构体
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 }
}
2.2.4 泛型类型枚举
enum Score<T> {
case point(T)
case grade(String)
}
let score0 = Score<Int>.point(100)
let score1 = Score.point(99)
let score2 = Score.point(99.5)
let score3 = Score<Int>.grade("A")
三、关联类型(Associated Type) 协议范型的表示方法
关联类型的作用:给协议中用到的类型定义一个占位名称
协议中可以拥有多个关联类型
protocol Stackable {
associatedtype Element // 关联类型
mutating func push(_ element: Element)
mutating func pop() -> Element
func top() -> Element
func size() -> Int
}class StringStack : Stackable {
// 给关联类型设定真实类型
// typealias Element = String
var elements = [String]()
func push(_ element: String) { elements.append(element) }
func pop() -> String { elements.removeLast() }
func top() -> String { elements.last! }
func size() -> Int { elements.count }
}
var ss = StringStack()
ss.push("Jack")
ss.push("Rose")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 }
}
四、类型约束 1.50
T : Person & Runnable 约束T 遵守Runnable并且是Person类型protocol Runnable { }
class Person { }
func swapValues<T : Person & Runnable>(_ a: inout T, _ b: inout T) {
(a, b) = (b, a)
}
protocol Stackable {
associatedtype Element : Equatable
}
class Stack<E : Equatable> : Stackable 1
typealias I
Element = E
}
protocol Stackable {
associatedtype Element: Equatable
}
class Stack<E : Equatable> : Stackable { typealias Element = E }
func equal<S1: Stackable, S2: Stackable>(_ s1: S1, _ s2: S2) -> Bool
where S1.Element == S2.Element, S1.Element : Hashable {
return false
}var stack1 = Stack<Int>()
var stack2 = Stack<String>()
// error: requires the types 'Int' and 'String' be equivalent
equal(stack1, stack2)
四、协议类型的注意点 2.00
protocol Runnable {}
class Person : Runnable {}
class Car : Runnable {}func get(_ type: Int) -> Runnable {
if type == 0 {
return Person()
}
return Car()
}var r1 = get(0)
var r2 = get(1)如果协议中有associatedtypeprotocol Runnable {
associatedtype Speed
var speed: Speed { get }
}
class Person : Runnable {
var speed: Double { 0.0 }
}
class Car : Runnable {
var speed: Int { 0 }
}
4.1 泛型解决
解决方案① :使用泛型
func get<T : Runnable>(_ type: Int) -> T {
if type == 0 {
return Person() as ! T
}
return Car() as ! T
}
var r1: Person = get(0)
var r2: Car = get(1)
4.2、不透明类型(Opaque Type)
func get(_ type: Int) -> some Runnable { Car() }
var r1 = get(0)
var r2 = get(1)
some限制只能返回一种类型
五、some
some 解决Runnable带有关联类型的报错的问题
some除了用在返回值类型上,一般还可以用在属性类型上
protocol Runnable { associatedtype Speed }
class Dog : Runnable { typealias Speed = Double }
class Person {
var pet: some Runnable {
return Dog()
}
}