Swift Tips 006 - Using a name already taken by the standard library
每天了解一点不一样的 Swift 小知识
代码截图
小笔记
这段代码想传达什么意思
试想一下,假设你定义了一个 Command 类型,针对命令执行错误的状况,我们八成会用一个继承自 Error 类型的子类来描述错误类型,那么问题来了,这个类型的名字叫什么?
看起来 Error 是最简单优雅的,所以你的代码可能会写成如下的样式
extension Command {
enum Error: Error {
case missing
case invalid(String)
}
}
只是很可惜,这么写代码会报错,一种解决方案就是我们把代码改成
extension Command {
enum CommandError: Error {
case missing
case invalid(String)
}
}
这种方案虽然解决了问题,但不够优雅,主要是在类型的命名上,当我们要引用 Command 里面的这个内嵌类型时,我们会怎么写代码呢,必定是 Command.CommandError.missing
,看到这似乎嗅到了一点点 Objective-C 那又臭又长的命名味道,那么有更好的解决方案么?
有,也就是我们说的第二种方案,用 Swift.
来解决与命名冲突的问题,此时代码就需要这么写了
extension Command {
enum Error: Swift.Error {
case missing
case invalid(String)
}
}
第二种方案是怎么解决问题的
在正式解释前,我们需要知道今天的话题本质是命名冲突引起的,当然,这里还需要明确一点的就是,命名冲突有 2 种情况,一种是类型之间的冲突,一种是 module 级别的冲突,
对于类型之间的冲突,是说同一个 Framework 下,如何区分同名类型,这个使用 Swift 里面的嵌套类型就可以解决,下面是一个示例
// A.framework
struct Command {
struct Error: Swift.Error {}
}
struct Result {
struct Error: Swift.Error {}
}
let commandError = Command.Error()
let resultError = Result.Error()
而对于 Module 级别的冲突,是说如何区分 A Framework 和 B Framework 里面的同名类型,这个问题在 Swift 里也有解决的办法,通过指明类型所在的 Framework 即可。
// A.framework
enum Error {
}
// B.framework
enum Error {
}
let aError = A.Error()
let bError = B.Error()
上面的内容或多或少都与 Swift 命名空间的知识相关联,而关于 Swift 命名空间的官方文档,我并没有找到太多,只找到 2 份比较有分量的材料,感兴趣的同学可以看看:
Swift 之父 Chris 在 Twitter 上的一些说明:传送门
Matt 大神在 SO 上的回答:传送门
结合今天的内容来总结一下上面 2 位大牛表达的观点就是说:
针对 module 级别的命名冲突,Swift 会优先考虑你代码中的命名,如果你想要使用其他库的命名,需要显示的指明,例如用 Swift.Error
或者 Foundation.NSArray
来指明这个类型不是当前代码中的类型。
好了,今天的内容就差不多了。
one more thing,还是关于命名空间的一些“技巧”,我们也可以在 Apple 公司新推出的 Combine 框架里看到,例如用 enum 做 namespace:传送门