一、錯誤類型
開發過程常見的錯誤
語法錯誤(編譯報錯)
邏輯錯誤
運行時錯誤(可能會導致閃退,一般也叫做異常)
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()
}
}