一. 什么是??
我们定义一个可选型的string做个测试
- 使用三目运算符
var name: String?
//case name = nil
name = nil
print(name != nil ? name! : "name为nil") //name为nil
//case name != nil
name = "Jobs"
print(name != nil ? name! : "name为nil") //Jobs
- 使用空合并运算符
var name: String?
//case name = nil
name = nil
print(name ?? "name为nil") //name为nil
//case name != nil
name = "Jobs"
print(name ?? "name为nil") //Jobs
发现??和三目运算符的功能是一样的,而且??使用更简单些
二. 真的这么简单吗?
定义两个函数
func getName(_ c: Character) -> String? {
print("执行了getName")
if c == "🐥" {
return "蔡徐坤"
}
return nil
}
func getDefalutName() -> String{
print("执行了getDefalutName")
return "defalutName"
}
- getName传入🐥,发现三目运算符打印了两次
getName
,而??只打印了一次。但是咱平时也不这么写,所以没啥区别,??能稍微简单点
//使用三目运算符
getName("🐥") != nil ? getName("🐥") : getDefalutName()
打印:
执行了getName
执行了getName
//使用??
getName("🐥") ?? getDefalutName()
打印:
执行了getName
- getName传入"",执行结果一模一样😂
//使用三目运算符
getName("A") != nil ? getName("A") : getDefalutName()
打印:
执行了getName
执行了getDefalutName
//使用??
getName("A") ?? getDefalutName()
打印:
执行了getName
执行了getDefalutName
总结:??和三目运算符的功能是一模一样的
三.??的实现
3.1 咱们自己实现一个
震惊,左边不为nil,应该用左边的值, 但是也执行了右边的getDefalutName函数。
这编译期不太行呀。
// 中缀运算符, ??? 接受两边的参数, 左边为可选型, 右边为非可选型
infix operator ???
func ??? <T>(optional :T?, defaultValue: T) -> T{
if let t = optional {
return t
}
return defaultValue
}
getName("🐥") ??? getDefalutName()
打印:
执行了getName
执行了getDefalutName
3.2 测试下系统的
换成??试试, 发现系统的??发现坐边不为nil的时候,就不执行getDefalutName()
,挺好的
getName("🐥") ?? getDefalutName()
打印:
执行了getName
看看系统的实现,发现系统使用了自动闭包, 那么我就懂了
//public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?) rethrows -> T?
3.3 改进我们的???
咱们也加个自动闭包。看看结果,真好呀真好,和系统的??
功能一样了
infix operator ???
func ??? <T>(optional :T?, defaultValue: @autoclosure () -> T) -> T{
if let t = optional {
return t
}
return defaultValue()
}
getName("🐥") ??? getDefalutName()
打印:
执行了getName
四.总结
- 相同点:空和并运算符和三目运算符的功能基本一致
- 不同点:空和并运算符只能用来判断前驱参数是否为nil,而三目运算符则更加强大些,能用来判断前驱参数是否为true。 所以
??
相当于?:
的子集 - 所以我们要根据情况适时选择到底该用他俩中的哪一个
- 系统的
??
使用了自动闭包延迟了后继参数的执行时间,值得我们学习