转载

Swift 中关于”??”操作符一些有意思的事情

Swift 中关于”??”操作符一些有意思的事情

转自: swiftcafe

Swift 的语法在保证安全和健壮的基础上,又带有很多非常灵活的特性,比如 ?? 操作符就是其中一个。大家可能已经了解它,也可能有些同学不了解它,这里给大家整理了关于这个操作符值得一看的讨论。

?? 操作符简述

在展开讨论之前,我们先来了解这个操作符的作用是什么。这个操作符和 Optional 相关,让我们来看一个例子:

var a:Int? print(a ?? 2) //2

?? 操作符的左边是一个 Optional 值,右边是一个普通值,它的作用就是,如果左边的 Optional 值为 nil, 那么就使用右边的普通值作为返回值,如果左边的 Optional 不为 nil,则返回左边的 Optional 解包后的值。

比如我们这个例子中,变量 a 的值是 nil,所以 print 语句输出的就是后面的默认值 2。

刚才那个例子,如果不用 ?? 操作符,那么就是这样的逻辑:

print(a == nil ? 2 : a)

让我们来看看 ?? 操作符的定义:

func ??          (optional: T?, @autoclosure defaultValue: () throws -> T) -> T    

把它简化一下,就是:

func ??          (optional: T?, defaultValue: () throws -> T) -> T    

它会接受一个 T 类型的 Optional 值,和一个 T 类型的普通值作为默认值, 然后返回一个解包后的 T 类型的值。这个最终返回值是要根据第一个参数是否为空来确定。

?? 操作符的一些讨论

?? 操作符可以让我们更方便的处理 Optional。 但它也存在一些小问题,Swift 的官方邮件组中大家就有讨论。比如这段代码:

func test(x: ((String) -> String)? = nil) {     let qf = { (p:String) -> String in return "tt" }     let fn = x ?? qf     fn("tt") }

test 函数它接受另外一个 Optional 类型的参数,然后在它内部用 ?? 操作符判断传进来的函数 x 是否为 nil,如果是,则用内部的另外一个函数 qf 来替代它。

初步来看,这样的代码应该没有什么问题,?? 的左边的变量类型是 ((String) -> String)?, 而右边的变量类型是 (String) -> String, 符合 ?? 操作符的定义。

但这段对 ?? 操作符的使用在实际编译的时候报错了。 我们必须把 qf 也声明成 Optional 类型,才可以使用:

let qf:((String) -> String)? = { (p:String) -> String in return "tt" } let fn = (x ?? qf)! fn("tt")

这样虽然编译通过了,但却不符合 ?? 操作符的定义了。这次两边都是 Optional 类型,而表达式的返回值也是 Optional。

这时关于 ?? 在 Swift 邮件组中最近的一个讨论,最后的一次回复说这是 Swift 的一个 bug, 将会在 Swift 3.0 版本中修复,这时这个回复的原文:

Swift 中关于”??”操作符一些有意思的事情

?? 操作符两边的类型可以不一样

在邮件组里,还有一个讨论,大家可以看一下这段代码:

var a:Int? print(a ?? "test")

注意哦,左边的 a 是 Int? 类型的, 而右边是一个 String 字符串。 这段代码在目前的 Swift 2.2 编译上是可以编译通过的。

这个话题讨论的比较多,比较合理的一个解释是 Swift 编译器会根据当前表达式所在的场景进行相应的向上转型。

比如作为 print 函数的参数,编译器会将它们都看做 Any 类型,因为 print 函数接受的就是 Any 类型的参数。所以这个语句可以编译通过。

如果这个语句用在这个场景,就会导致编译失败了:

let x = a ?? "test"

这次的场景,是将它的返回值赋值给 x。 但这次编译器就无法确定他们的通用类型,而将他们看做两个不同的类型,这样就导致编译失败了。

同样的,这段代码就可以编译通过:

let x = a ?? NSDate()

按照刚才的逻辑,那么因为后面的类型是 NSDate 它们都可以是 NSObject 的子类,所以编译器将它们都作为 NSObject 的通用类型来处理了。

而上面的那个例子 let x = a ?? "test" 因为后面的字符串是 Swift 中的 String 类型,编译器就无法找到它们的通用父类,就导致失败了。

当然这是目前的讨论和分析结果,大家也可以一起来思考,来讲你的分析与大家交流。

总结

这次我们一起讨论了 ?? 操作符的作用,以及它在使用中的一些要注意的点。从这几处中可以看到语言本身的很多细节。Swift 邮件组是一个非常好的资源,里面会有很多关于各个话题的深入讨论,而且都是完全开放的,大家也可以到它的主页申请访问: https://lists.swift.org/mailman/listinfo/swift-users

原文  http://www.cocoachina.com/swift/20160510/16200.html
正文到此结束
Loading...