• Swift 中关于”??”操作符


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

    1.jpg

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

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

    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 类型,编译器就无法找到它们的通用父类,就导致失败了。

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

  • 相关阅读:
    golang mongo-driver filter 构建--bson和golang基础类型
    ieda 修改选了files open in assoasiated applications
    golang 轻量实用的http.Request 参数解析库 paramParse
    scrapyd spiderkeeper docker部署
    python Scrapy google trends
    shell 删除代码中的注释
    php中文转阿拉伯数字
    php调用oracle带有out参数的存储过程
    Mongodb 副本与分片 学习笔记
    Mongodb 特殊索引和集合 学习笔记
  • 原文地址:https://www.cnblogs.com/YangFuShun/p/5497388.html
Copyright © 2020-2023  润新知