Error handling
Wrong type
Common mistakes during development are
- Syntax error (compilation error)
- Logical error
- Runtime error (may cause a flash back, also known as an exception)
- .
Custom error
You can customize runtime Error messages in Swift using the Error protocol
enum SomeError: Error {
case illegalArg(String)
case outOffBounds(Int, Int)
case outOfMemory
}
Copy the code
Throws a custom Error inside a function. Functions that may throw an Error must add a throws declaration
func divide(_ num1: Int, _ num2: Throws -> Int {if num2 == 0 {throw SomeError. IllegalArg ("0 cannot be used as a didier ")} return num1 / num2}Copy the code
You need to use a try to call a function that might throw an Error
var result = try divide(20, 10)
Copy the code
The condition in which an error message is thrown
Catch the do –
You can use do — catch to catch errors
do { try divide(20, 0) } catch let error { switch error { case let SomeError.illegalArg(msg): Print (" error ", MSG) default: print(" error ")}}Copy the code
After an Error is thrown, the next line of the try will stop running until the end of the scope
func test() { print("1") do { print("2") print(try divide(20, Print ("3")} catch let SomeError. IllegalArg (MSG) {print(" parameter error :", MSG)} catch let SomeError. OutOffBounds (size, index) {print(" subbounds :", "size=\(size)", "Index =\(index)")} catch SomeError. OutOfMemory {print(" memory overflow ")} catch {print(" other error ")} print("4")} test() //1 //2 // Parameter exception: 0 cannot be used as divisor //4Copy the code
The catch scope has error variables that can be caught by default
do {
try divide(20, 0)
} catch {
print(error)
}
Copy the code
Processing Error
There are two ways to handle Error
Catch an Error by do — catch
do {
print(try divide(20, 0))
} catch is SomeError {
print("SomeError")
}
Copy the code
If you do not capture an Error, add a throws declaration to the current function, and the Error is automatically thrown to the upper function
If the top-level function main still does not catch an Error, the program terminates
func test() throws {
print("1")
print(try divide(20, 0))
print("2")
}
try test()
// 1
// Fatal error: Error raised at top level
Copy the code
Calls to functions written inside the function without capturing errors are reported, while calls to functions written outside the function are not
Then we add do catch, because the catch Error is not detailed enough, we need to catch all the Error information
At this point we add throws
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 cannot be divisor ") // 4Copy the code
Or add a catch to catch all the other Error cases
func test() {
print("1")
do {
print("2")
print(try divide(20, 0))
print("3")
} catch let error as SomeError {
print(error)
} catch {
print("其他错误情况")
}
print("4")
}
test()
Copy the code
Take a look at the following sample code. What is output after execution
func test0() throws { print("1") try test1() print("2") } func test1() throws { print("3") try test2() print("4") } func test2() throws { print("5") try test3() print("6") } func test3() throws { print("7") try divide(20, 0) print("8") } try test0()Copy the code
The following is printed and an error message is thrown
try
You can use try? And the try! Call a function that might raise an Error, so you don’t have to deal with errors
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()
Copy the code
A and B are equivalent
var a = try? divide(20, 0)
var b: Int?
do {
b = try divide(20, 0)
} catch { b = nil }
Copy the code
rethrows
Rethrows indicates that the function itself does not throw an error, but calling the closure parameter does, and it will throw the error up
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)
Copy the code
Null merge operators are declared using REthrows
defer
The defer statement, which defines the code that must be executed before leaving the block in any way (throwing errors, returns, and so on)
The defer statement will be deferred until the end of the current scope
func open(_ filename: String) -> Int { print("open") return 0 } func close(_ file: Int) { print("close") } func processFile(_ filename: Throws {let file = open(filename) defer {close(file)} // Use file //..... } try processFile("test.txt") // open // close // Fatal error: Error raised at top levelCopy the code
The defer statements are executed in reverse of the order defined
func fn1() { print("fn1") } func fn2() { print("fn2") } func test() { defer { fn1() } defer { fn2() } } test() // fn2 // fn1Copy the code
It’s an assert.
Many programming languages have an assertion mechanism that throws runtime errors if specified conditions are not met. It is often used to determine conditions during debugging
By default, Swift assertions are only valid in Debug mode and ignored in Release mode
Added Swift Flags default behavior for modifying assertions
-assert-config Release
: forces assertion to be closed-assert-config Debug
: Forcibly enables assertion
fatalError
If you run into a serious problem and want to end the program, you can throw an error directly using the fatalError function
This is an error that cannot be caught by do — catch
With fatalError, there is no need to write return
Func test(_ num: Int) -> Int {if num >= 0 {return 1} fatalError("num must not be less than 0")}Copy the code
For some methods that you have to implement but don’t want others to call, consider using fatalError internally
class Person { required init() {} }
class Student: Person {
required init() {
fatalError("don't call Student.init")
}
init(score: Int) {
}
}
var stu1 = Student(score: 98)
var stu2 = Student()
Copy the code
Local scope
Local scopes can be implemented using DO
do {
let dog1 = Dog()
dog1.age = 10
dog1.run()
}
do {
let dog2 = Dog()
dog2.age = 10
dog2.run()
}
Copy the code
Generics
The basic concept
Generics parameterize types, increasing code reuse and reducing code volume
func swapValues<T>(_ a: inout T, _ b: inout T) {
(a, b) = (b, a)
}
var i1 = 10
var i2 = 20
swap(&i1, &i2)
print(i1, i2) // 20, 10
struct Date {
var year = 0, month = 0, day = 0
}
var d1 = Date(year: 2011, month: 9, day: 10)
var d2 = Date(year: 2012, month: 10, day: 20)
swap(&d1, &d2)
print(d1, d2) // Date(year: 2012, month: 10, day: 20), Date(year: 2011, month: 9, day: 10)
Copy the code
Generic functions assign values to variables
func test<T1, T2>(_ t1: T1, _ t2: T2) {}
var fn: (Int, Double) -> () = test
Copy the code
The generic type
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 intStack = Stack<Int>()
var stringStack = Stack<String>()
var anyStack = Stack<Any>()
Copy the code
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 } } 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()) / / 0Copy the code
enum Score<T> { case point(T) case grade(String) } let score0 = Score<Int>.point(100) let score1 = Score.point(99) let Score = Score. Point (99.5) let score3 = Score<Int>. Grade ("A")Copy the code
Associated Type
Associative types are used to define a placeholder name for the type used in the protocol
protocol Stackable {
associatedtype Element
mutating func push(_ element: Element)
mutating func pop() -> Element
func top() -> Element
func size() -> Int
}
struct Stack<E>: Stackable {
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
}
}
class StringStack: Stackable {
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")
Copy the code
A protocol can have multiple association types
protocol Stackable {
associatedtype Element
associatedtype Element2
mutating func push(_ element: Element)
mutating func pop() -> Element
func top() -> Element
func size() -> Int
}
Copy the code
Type constraints
protocol Runnable { }
class Person { }
func swapValues<T: Person & Runnable>(_ a: inout T, _ b: inout T) {
(a, b) = (b, a)
}
Copy the code
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>()
equal(stack1, stack2)
Copy the code
Note The protocol type
Take a look at the sample code below to analyze
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)
Copy the code
If the protocol has AssociatedType
protocol Runnable { associatedtype Speed var speed: Speed { get } } class Person: Runnable { var speed: Double {0.0}} class Car: Runnable {var speed: Int {0}}Copy the code
This will give you an error, because you don’t know at compile time what the real type of Speed is, okay
This can be solved with generics
protocol Runnable { associatedtype Speed var speed: Speed { get } } class Person: Runnable { var speed: Double {0.0}} class Car: Runnable {var speed: Int {0}} 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)Copy the code
You can also declare an opaque type using the some keyword
The some constraint can only return one type
protocol Runnable { associatedtype Speed var speed: Speed { get } } class Person: Runnable { var speed: Double {0.0}} class Car: Runnable {var speed: Int {0}} func get(_ type) Int) -> some Runnable { return Car() } var r1 = get(0) var r2 = get(1)Copy the code
In addition to returning value types, some can also be used for attribute types
protocol Runnable {
associatedtype Speed
}
class Dog: Runnable {
typealias Speed = Double
}
class Person {
var pet: some Runnable {
return Dog()
}
}
Copy the code
The nature of generics
We use the following example code to analyze its internal specific how to achieve
func swapValues<T>(_ a: inout T, _ b: inout T) {
(a, b) = (b, a)
}
var i1 = 10
var i2 = 20
swap(&i1, &i2)
print(i1, i2) // 20, 10
var d1 = 10.0
var d2 = 20.0
swap(&d1, &d2)
print(d1, d2) // 20.0, 10.0
Copy the code
After disassembly
From the point of view of calling both exchange methods, you end up calling the same function because the address of the function is the same; Int’s metadata and Double’s metadata are passed as parameters, so the metadata should specify the corresponding type to handle the metadata
The nature of alternatives
The nature of the optional nature is the enum type
We can look in the main file
The real way to write grammatical sugar is as follows
var age: Int? Var ageOpt0: Optional<Int> = Optional<Int>. Some (10) var ageOpt1: Optional = .some(10) var ageOpt2 = Optional.some(10) var ageOpt3 = Optional(10)Copy the code
var age: Int? Var ageOpt0: Optional<Int> =. None var ageOpt1 = Optional<Int>. NoneCopy the code
var age: Int? = .none
age = 10
age = .some(20)
age = nil
Copy the code
The use of optional options in switch
switch age { case let v? // Add? V print("some", v) case nil: print("none")} Switch age {case let.some (v): print("some", v) case. None: print("some", v) case. print("none") }Copy the code
Multiple options
var age_: Int? = 10
var age: Int?? = age_
age = nil
var age0 = Optional.some(Optional.some(10))
age0 = .none
var age1: Optional<Optional> = .some(.some(10))
age1 = .none
Copy the code
var age: Int?? = 10
var age0: Optional<Optional> = 10
Copy the code