这篇文章表明:??
非常耗时,测试发现它是真的。所以我想优化这个:
#if DEBUG
public func ?? <T>(left: T?, right: T) -> T {
guard let value = left else {
return right
}
return value
}
#endif
但
string = string ?? ""
错误:Ambiguous use of operator '??'
您链接到的文章没有描述nil
合并运算符如何"耗时">,而是陈述了一个简单的事实,即两个nil
合并运算符调用以及其他计算的复合表达式的构建时间明显比我们将复合表达式分解为更小的部分要长得多,特别是如果前者在某些时候包含惰性计算(例如,对于??
运算符的 RHS)。通过分解复杂的表达式,我们帮助编译器;众所周知,Swift 在编译复杂的复合语句时遇到了一些困难,所以这是意料之中的。但是,这通常不会影响运行时性能,前提是您为发布模式构建了实现。
通过在调试期间使用lhs
的非惰性计算重载??
运算符的实现,同时仍使用长复合语句,应该不会完全解决上一条中描述的问题。如果编译时间对您来说确实是一个问题,请使用与文章作者相同的方法;将复杂的表达式分解为几个较小的表达式,以便自己完成编译器的一些工作。重构的唯一目的是减少编译时间(我们可能认为这是编译器的工作,而不是我们的工作)有时可能会令人沮丧,但这是我们(目前)在使用 Swift 时必须面对的问题。例如,请参阅以下优秀的文章,介绍长/复杂表达式的 Swift 编译器问题:
- Swift 类型检查器中的指数时间复杂度
W.r.t. 歧义错误:作为参考,nil
coelescing 运算符的官方实现可以在这里找到:
@_transparent public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T { switch optional { case .some(let value): return value case .none: return try defaultValue() } }
我们可能会注意到,它与您自己的通用实现具有相同的特异性(比较签名!),这意味着歧义错误是意料之中的。 例如,与以下各项进行比较:
// due to the '@autoclosure', these two have the
// same precedecence/specificity in the overload
// resolution of 'foo(_:)'
func foo<T>(_ obj: T) {}
func foo<T>(_ closure: @autoclosure () -> T) {}
foo("foo") // ERROR at line 9, col 1: ambiguous use of 'foo'
当您实现您的??
重载它时,就像在您的答案中一样,键入Any
,此实现变得比通用实现更具体,这意味着它将优先于??
的重载分辨率。然而,使用Any
这样的上下文通常是一个坏主意,试图模仿动态类型而不是依赖 Swift 著名的静态类型。
如果我重载??
,我不能使用<T>
...
然后
#if DEBUG
public func ?? (left: Any?, right: Any) -> Any {
guard let value = left else {
return right
}
return value
}
#endif
没关系!
但是我无法获得返回值的类型...