2024-07-31 18:54:37
,某些文章具有时效性,若有错误或已失效,请在下方留言。错误基础知识
所有要抛出的错误都必须是符合Error
协议的枚举。
enum PasswordError: Error {
case empty
case short
}
错误可以添加关联值
enum PasswordError: Error {
case empty
case short
case obvious(message: String)
}
将函数或方法标记为可能抛出错误,请在其返回类型前添加 throws
。
func encrypt(_ str: String, with password: String) throws -> String {
// complicated encryption goes here
let encrypted = password + str + password
return String(encrypted.reversed())
}
然后,使用do
、try
和catch
的组合来运行有风险的代码。
do {
let encrypted = try encrypt("secret information!", with: "T4ylorSw1ft")
print(encrypted)
} catch PasswordError.empty {
print("You must provide a password.")
} catch PasswordError.short {
print("Your password is too short.")
} catch PasswordError.obvious {
print("Your password is obvious")
} catch {
print("Error")
}
要使用关联值,需要将其绑定到catch
代码块中的常量:
catch PasswordError.obvious(let message) {
print("Your password is obvious: \(message)")
}
在处理关联值时,还可以使用模式匹配。
enum PasswordError: Error {
case empty
case short(minChars: Int)
case obvious(message: String)
}
catch PasswordError.short(let minChars) where minChars < 5 {
print("We have a lax security policy: passwords must be at least \(minChars)")
} catch PasswordError.short(let minChars) where minChars < 8 {
print("We have a moderate security policy: passwords must be at least \(minChars)")
} catch PasswordError.short(let minChars) {
print("We have a serious security policy: passwords must be at least \(minChars)")
}
错误传播
如果函数 A 调用函数 B,而函数 B 调用函数 C,那么应该由谁来处理 C 抛出的错误呢?如果您的答案是 “B”,那么您现有的错误处理知识就足够了。
在functionB() 中捕获所有错误,如果想让functionA()对其下方发生的任何错误视而不见,就可以使用这个方案。
enum PasswordError: Error {
case empty
case short
case obvious
}
func functionA() {
functionB()
}
func functionB() {
do {
try functionC()
} catch {
print("Error!")
}
}
func functionC() throws {
throw PasswordError.short
}
functionB()忽略错误,让错误向上扩散到它自己的调用者,这就是所谓的错误传播。
enum PasswordError: Error {
case empty
case short
case obvious
}
func functionA() {
do {
try functionB()
} catch {
print("Error!")
}
}
func functionB() throws {
// 向上抛出错误
try functionC()
}
func functionC() throws {
throw PasswordError.short
}
将错误处理的一部分委托给最合适的函数。例如,您可能希望functionB()捕捉空密码,而functionA()处理所有其他错误。
func functionA() {
do {
try functionB()
} catch {
print("Error!")
}
}
func functionB() throws {
do {
try functionC()
} catch PasswordError.empty {
print("Empty password!")
}
}
func functionC() throws {
throw PasswordError.short
}
抛出函数作为参数
非抛出函数是抛出函数的子类型。因此,您可以在与抛出函数相同的地方使用非抛出函数。
enum Failure: Error {
case badNetwork(message: String)
case broken
}
func fetchRemote() throws -> String {
// complicated, failable work here
throw Failure.badNetwork(message: "Firewall blocked port.")
}
func fetchLocal() -> String {
// this won't throw
return "Taylor"
}
func fetchUserData(using closure: () throws -> String) {
do {
let userData = try closure()
print("User data received: \(userData)")
} catch Failure.badNetwork(let message) {
print(message)
} catch {
print("Fetch error")
}
}
fetchUserData(using: fetchLocal)
只需修改最后一行,就可以从本地获取转为远程获取
fetchUserData(using: fetchRemote)
当你想在抛出闭包参数时使用错误传播时,需要使用rethrows
关键字
func fetchUserData(using closure: () throws -> String) rethrows {
let userData = try closure()
print("User data received: \(userData)")
}
try try? try!
Swift 的错误处理有三种形式,三种形式都有其用途。它们都用于调用标记有throws
的函数,但意义却有细微的不同:
- 使用
try
时,必须有一个catch块来处理发生的任何错误。 - 使用
try?
时,如果出现任何错误,您调用的函数将自动返回nil。您不需要捕获它们,但需要注意您的返回值变成了可选值。 - 使用
try!
时,如果出现任何错误,函数将使应用程序崩溃。
使用 try?
意味着安全地解包其可选的返回值
if let savedText = try? String(contentsOfFile: "saved.txt") {
loadText(savedText)
} else {
showFirstRunScreen()
}
如果返回值为 nil
,您还可以使用空合操作符??
来使用默认值,从而完全消除可选性。
let savedText = (try? String(contentsOfFile: "saved.txt")) ?? "Hello, world!"
断言
断言允许声明某些条件必须为真。断言常见的两种形式
assert(condition: Bool)
assert(condition: Bool, message: String)
断言的基本使用
let success = runImportantOperation()
assert(success == true, "Important operation failed!")
先决条件
只有当应用程序在调试模式下运行时,才会检查断言,这在开发过程中非常有用,但在发布模式下会自动停用。如果您希望在发布模式下进行断言,考虑到失败会导致应用程序立即终止,您应该使用precondition()
代替。
func *(lhs: [Int], rhs: [Int]) -> [Int] {
precondition(lhs.count == rhs.count, "Arrays were not the same size")
var result = [Int]()
for (index, int) in lhs.enumerated() {
result.append(int * rhs[index])
}
return result
}
let a = [1, 2, 3]
let b = [4, 5]
let c = a * b
请记住,启用-Ounchecked会有效地禁用您的先决条件,但同时也会禁用其他边界校验。
Never 和 fatalError()
Never
返回类型,它的意思是 “这个函数永远不会返回”。这不同于Void,后者的意思是 “什么也不会返回”–Never函数实际上永远不会返回。
fatalError()
函数,该函数会立即终止应用程序,并可选择写出错误信息。
暂无评论内容