你有没有遇到过这样的情况呢?学校大作业 5 人一组,4 人划水,直接变成个人项目;工作里安排 2 个人一起协作,同事却觉得他只用负责加油。这些自顾自划水的人真是令人头疼...那么有什么好的应对方法吗?不学不知道,Swift 语言中的各种异常处理机制提供了一整套应对方案,让我们来看看它有哪些妙计!
初期应对方案
尽早记录分工安排,提前认出划水人——throws
throws
是 Swift 异常处理的重要关键词。它会出现在函数(包括成员函数、构造函数)的签名中,用来表示一个函数是否会抛出异常。换句话说,也就是这个函数会不会在其内部就处理完可能触发的异常。
// 可能抛出异常
func canThrowErrors() throws -> String
// 不会抛出异常
func cannotThrowErrors() -> String
带throws
的函数的学名叫 throwing function,如果我们把处理异常作为函数的工作,那么 throwing function 其实就是理直气壮的划水人——“我不干了,异常在这儿,你们帮我处理吧”。在 Swift 中,只有 throwing function 才可以抛出异常。有了 throws
这个关键词,相当于每个要划水的函数都要提前和编译器登记,不然就得自己做好异常处理的工作。
// Swift 通过实现遵从 Error 协议的枚举来实现自定义异常
enum SomeError: Error {
case missing // 常见划水行为,人间蒸发
case forgot // 常见划水行为,“我忘了”
}
func cannotThrowErrors() -> String {
// 普通函数抛出异常,编译器报错:
// Error is not handled because the enclosing function is not declared 'throws'
throw SomeError.missing
}
这就是 Swift 告诉我们的第一条对策:在初期要记录好分工,让不能及时完成的同学或者同事提前说出来,提前预防“这个是要我做的吗?没说过呀!”、“不是我划水啊,根本就没给我分配活儿呀”之类的常见借口。
另一方面,有了这个标记,编译器就可以区分 throwing funciton 类型和普通函数类型,当参数类型不是 throwing function 的时候,编译器就会限制传入会划水的函数。
// 输入类型为 () -> String
func recruit(member: () -> String) -> String {
...
}
// 编译器报错:
// Invalid conversion from throwing function of type '() throws -> String' to
// non-throwing function type '() -> String'
recruit(member: canThrowErrors)
注意,编译器只会限制 throwing function 类型转化为普通函数,反之则没有限制。毕竟,谁会讨厌一个不划水的伙伴呢?
// 输入类型为 () throws -> String
func recruit(member: () throws -> String) -> String {
...
}
// 无论传入的是普通函数还是 throwing function,都不会报错
recruit(member: canThrowErrors)
recruit(member: canNotThrowErrors)
同样,对我们的工作学习来说,如果不能接受有人划水,那么应该在上面提到的早期分工时尽快反馈,该换人换人,该换组换组。
可是往往,由于他们初期演技太好,我们还是在项目进行中陷入了有人撂挑子的情况,这该怎么办呢?别着急,Swift 还有不少对策。
中后期应对方案
被动技能:你划我也划
俗话说,打不过就加入。那些划水的人剩下的工作,为啥要我补上?谁爱干谁干!这个方案在 Swift 中表现的方式,就是一个 throwing function 可以继续向外传内部的 throwing function 调用可能会抛出的异常:
func canAlsoThrowErrors() throws -> String {
// 调用抛出的异常会作为 canAlsoThrowErrors 抛出的异常
// 留给 canAlsoThrowErrors 的调用者处理
return try canThrowErrors()
}
这里要注意,任何时候调用一个 throwing function 都需要在前面使用 try
关键词(包括后文会提到的 try?
和 try!
)。
佛系分工:做不成就做不成了吧——try?
另一种方法,就是在分配工作的时候,对可能划水的人的工作不抱期望。期望越大失望越大,那我没有期望,岂不是完全不会失望?在 Swift 中,根据这条方针提供了 try?
。使用try?
调用一个 throwing function 会返回一个可选类型(Optional),如果函数抛出异常,那么调用返回的就是nil
。
func whatever() -> String? {
return try? canThrowErrors()
}
暴躁路线:敢甩锅?那我们同归于尽——try!
Swift 也提供了一条同归于尽的方案,也就是 try!
。当使用 try!
调用 throwing function 时,一旦抛出异常,程序就直接终止了。不过和气生财,还是推荐在基本不会抛出异常的时候使用 try!
。
func bang() -> String {
return try! canThrowErrors()
}
没办法,得先收拾残局 qwq——do-catch
上面的几条虽然佛的佛,炸的炸,但是面对一些要紧的项目的时候,我们也只能暂时顶上,自己来干。对于 Swift 来说,应用中出现的异常往往是需要进行专门处理的,这就需要使用 do-catch
了。
do-catch
的使用语法和很多语言中的相似,在 do
作用域中尝试调用 throwing function,在 catch
作用域中捕获 do
作用域可能抛出的异常:
do {
let result: String = try canThrowErrors()
} catch {
print("Error: \(error)")
}
在 catch
作用域中,可以获得一个名为 error
的局部变量,用来详细查看异常。
因为在 Swift 中的异常是枚举类型,所以也提供了模式匹配的语法,可以让我们更方便地针对指定类型的异常进行特定的操作:
do {
result = try canThrowErrors()
} catch SomeError.missing {
...
} catch SomeError.forgot {
...
} catch {
// 只有没有匹配到的最后的 `catch` 中采用 `error` 变量哦
print("Error: \(error)")
}
有的时候,我们可能只愿意去完成空出来的工作的一部分。Swift 也给了这样的功能的支持。do-catch
可以不遍历所有的异常情况,但是因为这样仍可能有没处理的异常,所以这种 do-catch
需要被包在 throw function 中:
func canAlsoThrowErrors() throws -> String {
let result: String
do {
result = try canThrowErrors()
} catch SomeError.missing {
// 人没了也没办法,祝他一路走好吧...
result = "done by myself..."
}
return result
}
看到这里,你是不是对如何处理划水份子有了些心得呢?如果本文也能帮你学会 Swift 的异常处理机制,那就再好不过了。最后,祝大家在新的一年里,合作的组员都是勤勤恳恳的技术大牛,工作顺心,项目顺利!
最后推荐个我的iOS交流群:[891 488 181]
'有一个共同的圈子很重要,结识人脉!里面都是iOS开发,全栈发展,欢迎入驻,共同进步!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)
以下资料在群文件可自行下载**
欢迎大家入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!**