美文网首页Swift
Swift-奇奇怪怪的符号

Swift-奇奇怪怪的符号

作者: 琦玉老师很强 | 来源:发表于2021-06-29 15:57 被阅读0次

    相信大家在学习和使用Swift的时候,肯定会被各种各样的符号弄得摸不清头脑,不知道实际的原理是什么,这里简单介绍一下swift中常用的一些符号定义:

    ?- 可选类型

    ? 在Swift语言中对一种可选类型(Optional)操作的语法糖,可以用 ? 来表示“有一个值,值是什么”或者“没有值,值是nil”。

    var A_nil: Int? = nil
    var A: Int? = 10
    var A_other: Int = nil // 'nil' cannot initialize specified type 'Int'
    

    这里我们可以看到可选类型的变量既可以设置为有值类型,也可以设置为nil类型,但是A_other设置为nil,程序就会报错,nil不能初始化指定类型Int。

    那可选类型的本质是什么呢?

    var A: Int? = 10
    var A_optional: Optional<Int> = 10
    

    这里我们可以发现,类型(Optional)的定义就是通过在类型声明后加一个 ? 操作符完成的,它们的作用是等价的,我们可以看看Optional底层的源码是什么样的,这里是swift5的源码:

    @frozen public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    
        /// The absence of a value.
        ///
        /// In code, the absence of a value is typically written using the `nil`
        /// literal rather than the explicit `.none` enumeration case.
        case none
    
        /// The presence of a value, stored as `Wrapped`.
        case some(Wrapped)
    
        ...
    
        /// The wrapped value of this instance, unwrapped without checking whether
        /// the instance is `nil`.
        ///
        /// The `unsafelyUnwrapped` property provides the same value as the forced
        /// unwrap operator (postfix `!`). However, in optimized builds (`-O`), no
        /// check is performed to ensure that the current instance actually has a
        /// value. Accessing this property in the case of a `nil` value is a serious
        /// programming error and could lead to undefined behavior or a runtime
        /// error.
        ///
        /// In debug builds (`-Onone`), the `unsafelyUnwrapped` property has the same
        /// behavior as using the postfix `!` operator and triggers a runtime error
        /// if the instance is `nil`.
        ///
        /// The `unsafelyUnwrapped` property is recommended over calling the
        /// `unsafeBitCast(_:)` function because the property is more restrictive
        /// and because accessing the property still performs checking in debug
        /// builds.
        ///
        /// - Warning: This property trades safety for performance.  Use
        ///   `unsafelyUnwrapped` only when you are confident that this instance
        ///   will never be equal to `nil` and only after you've tried using the
        ///   postfix `!` operator.
        @inlinable public var unsafelyUnwrapped: Wrapped { get }
    
        ...
    }
    
    

    这里截取了一部分,我们可以看到:

    • Optional其实是一个标准库的一个enum类型
    • <Wrapped>泛型,也就是包装实际类型的数据类型
    • 遵守ExpressibleByNilLiteral,允许nil来初始化一个类型
    • none 和some两种类型,Optional.none就是nil , Optional.some就是实际值
    • 泛型属性unsafelyUnwrapped,理论上就是调用unsafelyUnwrapped获取可选项的实际的值,这种值不安全的
      这里我们用枚举来匹配对应的值:
    var A: Int? = 10
    
    // 匹配有值和没值两种情况
    switch A {
        case .none:
            print("nil")
        case .some(let value):
            print(value)
    }
    

    ! - 强制解析

    当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号!来获取值,这个感叹号表示"可选值有值"。我们称之为强制解析(forced unwrapping)

    var A: Int? = 10
    
    print(A!)
    print(A.unsafelyUnwrapped)
    

    这里我们看到A的值都能打印出10,unsafelyUnwrapped和强制解包操作符 ! 具有相同的值,但是unsafelyUnwrapped是不安全的

    var A: Int? = nil
    
    print(A!)
    print(A.unsafelyUnwrapped)
    

    这里都会出现崩溃,崩溃的信息分别是

    Fatal error: Unexpectedly found nil while unwrapping an Optional value
    // 致命错误:解包装可选值时意外发现nil
    Fatal error: unsafelyUnwrapped of nil optional
    // 致命错误:无可选的不安全解包
    

    根据报错的信息我们可以看到unsafelyUnwrapped是不安全的解包,这里我们看一下官方文档的解释:


    unsafelyUnwrapped官方解释

    这里我们来验证一下unsafelyUnwrapped的不安全性,首先我们先设置一下 -O 这个优化级别,这里的-O是指target -> Build Setting -> Optimization Level设置成-O时。release模式默认是Fastest, Smallest[-Os],将运行模式调整为release模式,按照下面代码重新运行:

    
    let A: Int? = nil
    
    print(A!)
    // print(A.unsafelyUnwrapped)
    
    print("----")
    
    

    此时出现崩溃,我们通过打印,可以看到相关的错误信息,说明强制解析可以检查确保当前的实际的值,错误信息也会正常打印检测:


    截屏

    相反我们使用unsafelyUnwrapped:


    截屏

    这里我们可以看到并没有出现崩溃信息,没有检查确保当前实际的值,这种是很不安全的。

    ??- 空合运算符

    空合运算符(a ?? b)将可选类型a进行空判断,如果a包含一个值就进行解包,否则就返回一个默认值b。表达式a必须是Optional类型。默认值b的类型必须要和a存储值的类型保持一致。
    我们可以用三元运算符表达:

    a != nil ? a! : b
    

    以上是一个三元运算符,当可选值a不为空时,对其进行强制解包a!以访问a中的值;反之默认返回b的值。如果b是一个函数,也就不会执行了。
    我们点开源码分析一下:

    ...
    public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T
    
    ...
    public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?) rethrows -> T?
    

    通过源码,我们可以看到:

    • <T>泛型
    • 第一个变量optional,是一个T?类型
    • 第二个变量defaultValue,默认值,通过@autoclosure修饰,自动转换成了一个闭包。比如"X",就会被封装成{return "X"},这里defaultValue不是一个值,而是一个函数,这样一来,我们可以在需要的时候再执行这个函数,而不需要的话,就不执行。
      如果我们这里设置defaultValue是T类型呢?我们自定义自己的符号运算符???来验证这个问题:
    func A() -> String{
        print("A is called!!!")
        return "A"
    }
    
    func B() -> String{
        print("B is called!!!")
        return "B"
    }
    
    infix operator ???
    
    func ???<T>(optional: T? , defaultValue: T) -> T{
        if let value = optional{ return value }
        return defaultValue
    }
    
    let AorB = A() ??? B()
    

    结果是:

    A is called!!!
    B is called!!!
    

    很显然A和B都执行了,不符合空值运算符的特性,然后我们将defaultValue设置为闭包类型,用@autoclosure修饰:

    func A() -> String{
        print("A is called!!!")
        return "A"
    }
    
    func B() -> String{
        print("B is called!!!")
        return "B"
    }
    
    infix operator ???
    
    func ??? <T>(optional: T?, defaultValue: @autoclosure () -> T) -> T {
        if let value = optional{ return value}
        return defaultValue()
    }
    
    let AorB = A() ??? B()
    let AorX = A() ??? "X"
    

    这里的结果如下:

    A is called!!!
    A is called!!!
    

    可以看到,optional执行后,defaultValue就不会执行,如果你本身就是闭包(或者函数),非常好,你将享受这种延迟调用带来的优势;但如果你不是闭包,你将被自动封装成闭包,同时也享受了这种延迟调用带来的性能提升!,这里"X"就会自动被封装成{return "X"},如果这里的X不是string类型,而是其他类型,空合运算符前后数据类型不一致,就会导致崩溃。

    $0- 闭包简化参数名

    Swift 自动对行内闭包提供简写实际参数名,可以通过$0$1$2 等名字来引用闭包的实际参数值。

    let numbers = [1,2,3,4,5]
    
    let sortNum = numbers.sorted(by: {(a, b) -> Bool in
        return a > b
    })
    print(sortNum)
    
    // 尾随闭包写法
    let sortNum2 = numbers.sorted { (a, b) -> Bool in
        return a > b
    }
    print(sortNum2)
    
    let sortNum3 = numbers.sorted(by: { $0 > $1 })
    print(sortNum3)
    
    // 尾随闭包写法
    let sortNum4 = numbers.sorted { $0 > $1 }
    print(sortNum4)
    

    简写实际参数名的数字和类型将会从期望的函数类型中推断出来。 in 关键字也能被省略,因为闭包表达式完全由它的函数体组成

    var myDict: [String: Int] = ["Li": 22,"Zhang": 28]
    myDict.sorted {
        let key1 = $0.0
        let value1 = $0.1
        let key2 = $1.0
        let value2 = $1.1
        print(key1,value1,key2,value2)
        return true
    }
    

    $0是传递给closure的第一个参数的简写名称,在这种情况下,当您映射一个Dictionary时,该参数是一个元组,因此$0.0是第一个一个键,$0.1是第一个值
    这里的输出结果是:

    Li 22 Zhang 28
    

    相关文章

      网友评论

        本文标题:Swift-奇奇怪怪的符号

        本文链接:https://www.haomeiwen.com/subject/ffihultx.html