Swift 中关于”??”操作符

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 版本中修复,这时这个回复的原文:

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

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

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 中关于”??”操作符的更多相关文章

  1. Swift中的as操作符

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交 ...

  2. [翻译]理解Swift中的Optional

    原文出处:Understanding Optionals in Swift 苹果新的Swift编程语言带来了一些新的技巧,能使软件开发比以往更方便.更安全.然而,一个很有力的特性Optional,在你 ...

  3. Swift 中范围和区间如何使用?

    虽然现在swift语言已经发展到了2.0版了,但是相信很多学习iOS开发的童鞋仍对swift语言存在各种各样的疑问,今天小编将为大家详细介绍swift中的范围和区间,下面我们一起来看看吧. Range ...

  4. Swift中的Void类型与空元祖表达式

    可能有不少Swift开发者会忽略这么一个细节:在Swift中,Void类型其实是一个别名类型,而其真正的类型为(),即一个空元祖(empty tuple)! 这种语言特性给Swift带来了一些比较方便 ...

  5. Swift Explore - 关于 Swift 中的 isEqual 的一点探索

    在我们进行 App 开发的时候,经常会用到的一个操作就是判断两个对象是否相等.比如两个字符串是否相等.而所谓的 相等 有着两层含义.一个是值相等,还有一个是引用相等.如果熟悉 Objective-C ...

  6. Swift Tips - 在 Swift 中自定义下标访问

    Untitled Document.md input[type="date"].form-control,.input-group-sm>input[type="d ...

  7. Swift中使用构建配置来支持条件编译-b

    在Objective-C中,我们经常使用预处理指令来帮助我们根据不同的平台执行不同的代码,以让我们的代码支持不同的平台,如: 1 2 3 4 5 6 7 8 9 #if TARGET_OS_IPHON ...

  8. 深圳尚学堂:Swift中的“!”和“?”

    Swift中的"!"和"?"Swift,苹果于2014年WWDC发布的新开发语言,用于搭建基于苹果平台的应用程序.Swift是一款易学易用的编程语言,而且它还是 ...

  9. Swift中的"可溢出"算术运算符

    大家知道Swift中拥有和C,Objc类似的算术运算符,它们分别是: + - * / % 但是你可能不知道这些Swift中的运算符和C,Objc语言中的有一个很大的不同之处,就是它们不可以被" ...

随机推荐

  1. Python实现抓取CSDN博客首页文章列表

    1.使用工具: Python3.5 BeautifulSoup 2.抓取网站: csdn首页文章列表 http://blog.csdn.net/ 3.分析网站文章列表代码: 4.实现抓取代码: __a ...

  2. 编程进阶:Java小白的序列化Serializable接口

    在之前的学习过程中,我们知道了如何使用FileInputStream输入流和FileOutputStream输出流编写程序读写文件. 下面我们来学习一下如何使用序列化和反序列化读写文件. 一.序列化 ...

  3. select, poll, epoll笔记

    看网络通信框架,netty, thrift,java nio等,最后都会通过select, poll, epoll或者socket等进行通信.查了些网页,总结一下.做个笔记 1. Socket单线程阻 ...

  4. js系列之js简介

    该系列教程都来源于:廖雪峰老师的博客 JavaScript是世界上最流行的脚本语言,因为你在电脑.手机.平板上浏览的所有的网页,以及无数基于HTML5的手机App,交互逻辑都是由JavaScript驱 ...

  5. nodejs进阶(7)—async异步流程控制

    Async介绍 Async是一个流程控制工具包,提供了直接而强大的异步功能.基于Javascript为Node.js设计,同时也可以直接在浏览器中使用. Async提供了大约20个函数,包括常用的 m ...

  6. 有关table布局时tr 属性display:block显示布局错乱

    display:block display:block是可以把非块级元素强制转换为块级元素显示,如内嵌元素span,原来不支持设置宽高,宽度是由内容撑开的; display:table-row tab ...

  7. Python学习系列----第五章 模块

    5.1 如何引入模块 在Python中用关键字import来引入某个模块,比如要引用模块math,就可以在文件最开始的地方用import math来引入.在调用math模块中的函数时,必须这样引用: ...

  8. Java—集合框架 List的 indexOf()、lastIndexOf()

    集合中某个元素出现的位置—List的indexOf(),lastIndexOf() indexOf(Object obj)方法的实现机制是从序列(List)的第0个元素开始依次循环,并且调用每个元素的 ...

  9. 最重要的“快捷键” IntelliJ IDEA

    转载:http://www.youmeek.com/intellij-idea-part-iii-hotkeys-explain/ @IntelliJ IDEA第三部分视频教程:最重要的“快捷键”专讲 ...

  10. jQuery的datatable的destroy属性,和$("#test").dataTable().fnDestroy();区别,两者的区别

    jQuery的datatable的destroy属性,和$("#test").dataTable().fnDestroy();区别,两者的区别. 1 destroy属性是,销毁实例 ...