书籍链接:《Pro Swift》 (链接需要梯子才能打得开)。
一、rethrows
的使用
我们先来看看Swift文档里的一句话:不会抛出错误的方法,属于会抛出错误的方法的一种。我们举个例子:
func definitelyWontThrow() {
print("Shiny!")
}
try definitelyWontThrow()
definitelyWontThrow()
这个方法不会抛出错误,但是我们也可使用try
去调用,虽然Xcode会有警告。这也证明了Swift文档的那句话。
下面我们通过例子来说明rethrows
的使用:假设我们有一个应用要获取用户的数据,可从服务器或者本地获取。我们定义一个Failure
枚举来列出可能的错误,并写两个方法分别获取服务器和本地的数据:
enum Failure: Error {
case badNetwork(message: String)
case broken
}
func fetchRemote() throws -> String {
// 从服务器获取数据,可能会有错误
throw Failure.badNetwork(message: "Firewall blocked port.")
}
func fetchLocal() -> String {
// 本地获取,不会抛出错误
return "Taylor"
}
然后我们再写一个方法来统一获取用户的数据,可以把fetchRemote()
和fetchLocal()
作为closure参数传进去:
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")
}
}
虽然这个方法要求传入的参数是会抛出错误的closure,但是我们之前说过:不会抛出错误的方法,属于会抛出错误的方法的一种,所以把不会抛出错误的fetchLocal()
作为参数传入fetchUserData(using closure: () throws -> String)
也是没有问题的。我们就可以这样使用:
fetchUserData(using: fetchLocal)
// 或者
fetchUserData(using: fetchRemote)
如果我们不想在fetchUserData(using closure: () throws -> String)
方法里面处理错误,而是继续抛出错误,让它的使用者去处理,可以在方法后面加上throws
关键字:
func fetchUserData(using closure: () throws -> String) throws {
let userData = try closure()
print("User data received: \(userData)")
}
在使用的时候就要处理错误:
do {
try fetchUserData(using: fetchLocal)
} catch Failure.badNetwork(let message) {
print(message)
} catch {
print("Fetch error")
}
现在问题来了!!!当我传入fetchLocal()
这个不会抛出错误的方法,根本就没有必要使用try/catch
。这时我们可以使用rethrows
来解决这个问题:
func fetchUserData(using closure: () throws -> String) rethrows {
let userData = try closure()
print("User data received: \(userData)")
}
这时我们如果我们传入fetchLocal()
,根本不需要处理抛出错误的问题:
fetchUserData(using: fetchLocal)
如果是传入fetchRemote()
,需要处理抛出的错误:
do {
try fetchUserData(using: fetchRemote)
} catch Failure.badNetwork(let message) {
print(message)
} catch {
print("Fetch error")
}
二、try
vs try?
vs try!
处理Swift的抛出错误,有三种方式:
-
try
: 必须与catch
配合使用 -
try?
: 如果调用的方法抛出了错误,那么会自动返回nil
;如果没有错误,那么返回带有值的可选类型 -
try!
: 如果调用的方法抛出错误的话,应用会crash;如果没有错误,那么返回对应的值。
我们该如何选择?1. 如果我们关心抛出了什么错误,使用try
;2. 如果我们不关心抛出的错误,并且不确定是否会抛出错误,使用try?
;3. 如果我们不关心抛出的错误,并且确定不会抛出错误,或者如果抛出了错误就让应用crash,使用try!
。
三、断言
当我们在编写复杂应用时,断言是很有好处的。可以让程序满足某些条件时,才继续向下执行。例如:
let success = runImportantOperation()
assert(success == true, "Important operation failed!")
我们先来看看assert
方法的定义:
public func assert(_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String = String(),
file: StaticString = #file,
line: UInt = #line) {
if _isDebugAssertConfiguration() {
if !_branchHint(condition(), expected: true) {
_assertionFailed("assertion failed", message(), file,
line, flags: _fatalErrorFlags())
} }
}
后面的两个参数file
和line
,Swift已经提供了默认值,分别代表当前文件和触发断言的行数。前面的condition
和message
参数都用了@autoclosure
,这意味着这两个closure不会马上调用。我们从方法的实现可以看到,_isDebugAssertConfiguration()
只有在debug模式下,才会运行里面的代码;然后!_branchHint(condition(), expected: true)
再运行我们传入的condition
,如果结果不是true
,运行_assertionFailed
。
四、Never
和fatalError()
Never
是一个很特殊的返回值类型,意思是这个方法永远不会返回;不同于Void
,Void
意思是返回空的东西。
fatalError()
会马上终止应用,它的返回值类型正是Never
。fatalError()
可以替代无意义的返回,例如,我们在使用UITableView
时,在cellForRowAt
方法里面,首先会从队列里面取出已有的cell,得到一个UITableViewCell
,然后在把它转成自定义的cell(MyCustomTableViewCell
),最后再把cell返回。假如转型失败呢?通常我们是直接返回UITableViewCell()
。但是理论上来说,如果转型失败的话,意味着代码出现了很严重的问题,我们返回UITableViewCell()
也是无意义的,所以我们也可以直接在转型失败的时候调用fatalError()
,不返回任何东西。
完
有任何问题,欢迎大家留言!
欢迎加入我管理的Swift开发群:536353151
,本群只讨论Swift相关内容。
原创文章,转载请注明出处。谢谢!