美文网首页
Swift 5 新增了什么?(上)

Swift 5 新增了什么?(上)

作者: 全站工程师 | 来源:发表于2019-03-29 23:45 被阅读0次

    好消息!Swift 5 终于随 Xcode 10.2 发布了!这个版本提供了稳定的 ABI 和一些众望所归的特性。
    在这篇教程中,你将了解 Swift 5 中最重要的变化。Swift 5 要求 Xcode 10.2 的支持,请确保在开始使用前先安装或更新 Xcode。

    准备开始

    Swift 5 与 Swift 4.2 源码级兼容,但和所有旧版本二进制不兼容。不过得益于 ABI 已稳定,未来的版本将能够保持二进制兼容。
    ABI 稳定使得基于不同 Swift 版本编译的 App 或库之间二进制兼容。Swift 标准库和运行时将嵌入到操作系统,因此 App 无需再将这些库打包一份发布。这将更利于工具解耦和操作系统集成。
    ABI 稳定还是跨 Swift 版本发布二进制框架的必要条件,因为这要求模块格式的稳定性,以便让包含框架公开接口的模块文件格式稳定。
    你会在本教程的各个段落看到类似 [SE-0001] 的这种 Swift 演化建议编号,可以通过超链接查看每个新变化的详细情况。

    学习本教程最好的方式是在 Xcode playground 里尝试这些新特性。
    启动 Xcode 10.2 并选择 File ▸ New ▸ Playground,选择 iOS 平台及空(Blank)模板。给它命名字并保存在你希望的任何地方,然后正式开始!

    什么是 ABI?
    ABI (Application Binary Interface) 即应用程序二进制接口:它描述了应用程序和操作系统之间、应用与其它的库和组件之间、或者应用的组成部分之间的交互接口。
    ABI 定义了底层的细节,比如函数如何调用,数据如何在内存中布局,元数据在哪里以及如何访问等等。

    语言改进

    Swift 5 引入了许多语言特性,例如动态可调用类型(dynamic callable types)等等。

    测试整数倍

    在 Swift 4.2 种,使用求余运算符确定一个数字是否另一个数字的倍数:

    let firstNumber = 4
    let secondNumber = 2
    if secondNumber != 0 && firstNumber % secondNumber == 0 {
      print("\(secondNumber) * \(firstNumber / secondNumber) = \(firstNumber)")
    }
    

    看看这段代码如何工作:

    1. 检查 secondNumber 是否为 0;
    2. 检查 firstNumber 除以 secondNumber 返回的余数是否为 0;
    3. 执行除法运算。
      你必须检查 secondNumber 是否为 0,因为如果是的话 % 将抛出错误。

    Swift 5 通过向 BinaryInteger 增加 isMultiple(of:) 方法来简化这种操作[SE-0225]:

    if firstNumber.isMultiple(of: secondNumber) {
      print("\(secondNumber) * \(firstNumber / secondNumber) = \(firstNumber)")
    }
    

    isMultiple(of:) 即使参数传 0 也能工作,而且最终代码更加清晰。

    原始字符串

    Swift 4.2 使用转义序列来表示字符串中的反斜杠引号

    let escape = "You use escape sequences for \"quotes\"\\\"backslashes\" in Swift 4.2."
    let multiline = """
                    You use escape sequences for \"\"\"quotes\"\"\"\\\"\"\"backslashes\"\"\"
                    on multiple lines
                    in Swift 4.2.
                    """
    

    Swift 5 增加了原始字符串,在字符串的开头和结尾添加 # 以便直接使用反斜杠和引号而不会出现问题。[SE-0200]:

    let raw = #"You can create "raw"\"plain" strings in Swift 5."#
    let multiline = #"""
                    You can create """raw"""\"""plain""" strings
                    on multiple lines
                    in Swift 5.
                    """#
    

    在原始字符串中使用字符串插值时,必须在反斜杠后面使用 # 号:

    let track = "Nothing Else Matters"
    print(#"My favorite tune\song is \#(track)."#)
    

    在某些情况下,需要在字符串的开头和结尾使用多个#:

    let hashtag = ##"You can use the Swift "hashtag" #swift in Swift 5."##
    

    在上面的代码中,需要在 hashtag 前后增加 ##,如此就可以在字符串中使用 #。字符串开头使用的 # 个数必须与字符串末尾的 # 匹配。

    Swift 4.2 中,在正则表达式中转义反斜杠,如下所示:

    // 1
    let versions = "3 3.1 4 4.1 4.2 5"
    let range = NSRange(versions.startIndex..., in: versions)
    // 2
    let regex = try! NSRegularExpression(pattern: "\\d\\.\\d")
    // 3
    let minorVersions = regex.matches(in: versions, range: range)
    // 4
    minorVersions.forEach { print(versions[Range($0.range, in:  versions)!]) }
    

    来看看代码如何工作的:

    1. 声明 versions 并定义一个覆盖整个字符串范围的 range
    2. 定义一个与 versions 中所有 Swift 子版本匹配的正则表达式;
    3. 使用 matches(in:options:range:) 检测子版本范围;
    4. 使用该范围从 versions 中获取子版本。

    Swift 5 使用原始字符串简化了正则表达式:

    let regex = try! NSRegularExpression(pattern: #"\d\.\d"#)
    

    在这段代码中,只使用了一半数量的反斜杠编写正则表达式,因为无需要转义原始字符串中的反斜杠。

    使用新字符属性

    Swift 4.2 在处理字符 (Character) 时需要采用一些常用的变通方法:

    let id = "ID10"
    var digits = 0
    id.forEach { digits += Int(String($0)) != nil ? 1 : 0 }
    print("Id has \(digits) digits.")
    

    这段代码中,为了检测 id 中有多少个数字,要先将 character 转换为 String 再转成 Int
    但是,Swift 5 为 Character 添加了属性,使字符更易于使用[SE-0221]:

    id.forEach { digits += $0.isNumber ? 1 : 0 }
    

    在这种情况下,使用 isNumber 来检查每个字符是否为数字。可以查看演化建议了解其他更多属性。

    使用新的 Unicode 标量属性

    在 Swift 4.2 中,可以为 Unicode 标量 (scalar) 实现文本处理算法,如下例:

    let username = "bond007"
    var letters = 0
    username.unicodeScalars.forEach { 
      letters += (65...90) ~= $0.value || (97...122) ~= $0.value ? 1 : 0
    }
    print("Username has \(letters) letters.")
    

    这段代码里,通过检查每个字符的 unicode 标量是否以大写或小写字母表示来计算 username 中有多少个字母。

    Swift 5 给 unicode 标量增加了属性,可简化文本处理[SE-0211]:

    username.unicodeScalars.forEach { letters += $0.properties.isAlphabetic ? 1 : 0 }
    

    这段代码中使用了 isAlphabetic 来检查每个字符是否为字母。可以查看演化建议了解其他更多属性。

    消除子序列

    Swift 4.2 从 Sequence 自定义点返回 SubSequence

    extension Sequence {
      func remove(_ s: String) -> SubSequence {
        guard let n = Int(s) else {
          return dropLast()
        }
        return dropLast(n)
      }
    }
    
    let sequence = [5, 2, 7, 4]
    sequence.remove("2") // [5, 2]
    sequence.remove("two") // [5, 2, 7]
    

    s 如果是个 Int 或者最后一个元素的情况下,remove(_:) 从序列中丢弃最后 n 个元素。

    Swift 5 用具体类型替换SubSequence[SE-0234]:

    extension Sequence {
      func remove(_ s: String) -> [Element] {
        guard let n = Int(s) else {
          return dropLast()
        }
        return dropLast(n)
      }
    }
    

    这段代码中,由于 dropLast()dropLast(_:) 返回 [Element],所以 remove(_:)返回 [Elements]

    Dictionary 更新

    Swift 5 为字典 (Dictionary) 带来了一些大家期待已久的功能:

    Compacting Dictionaries

    Swift 4.2 可使用 mapValuesfilterreduce 来过滤字典中的 nil 值:

    let students = ["Oana": "10", "Nori": "ten"]
    let filterStudents = students.mapValues(Int.init)
      .filter { $0.value != nil }
      .mapValues { $0! }
    let reduceStudents = students.reduce(into: [:]) { $0[$1.key] = Int($1.value) }
    

    这段代码使用了 mapValuesfilter/reduce 来检测确定具有有效成绩的学生。两种方法都需要多遍字典操作,使代码复杂化。

    Swift 5 使用 compactMapValues(_:) 实现更高效的解决方案[SE-0218]:

    let mapStudents = students.compactMapValues(Int.init)
    

    它在更少的代码行中完成了同样的事情,非常简洁!

    重命名 DictionaryLiteral

    Swift 4.2 使用 DictionaryLiteral 来声明字典:

    let pets: DictionaryLiteral = ["dog": "Sclip", "cat": "Peti"]
    

    DictionaryLiteral 不是一个字典或字面量,而是一组键-值对的列表。
    Swift 5 将 DictionaryLiteral 重命名为 KeyValuePairs[ SE-0214 ]:

    ### let pets: KeyValuePairs = ["dog": "Sclip", "cats": "Peti"]
    

    Numeric Protocol 更新

    Swift 4.2 引入了可用于向量的 Numeric 协议:

    // 1
    struct Vector {
      let x, y: Int
      
      init(_ x: Int, _ y: Int) {
        self.x = x
        self.y = y
      }
    }
    
    // 2
    extension Vector: ExpressibleByIntegerLiteral {
      init(integerLiteral value: Int) {
        x = value
        y = value
      }
    }
    
    // 3
    extension Vector: Numeric {
      var magnitude: Int {
        return Int(sqrt(Double(x * x + y * y)))
      }  
    
      init?<T>(exactly value: T) {
        x = value as! Int
        y = value as! Int
      }
      
      static func +(lhs: Vector, rhs: Vector) -> Vector {
        return Vector(lhs.x + rhs.x, lhs.y + rhs.y)
      }
      
      static func +=(lhs: inout Vector, rhs: Vector) {
        lhs = lhs + rhs
      }
      
      static func -(lhs: Vector, rhs: Vector) -> Vector {
        return Vector(lhs.x - rhs.x, lhs.y - rhs.y)
      }
      
      static func -=(lhs: inout Vector, rhs: Vector) {
        lhs = lhs - rhs
      }
      
      static func *(lhs: Vector, rhs: Vector) -> Vector {
        return Vector(lhs.x * rhs.y, lhs.y * rhs.x)
      }
      
      static func *=(lhs: inout Vector, rhs: Vector) {
        lhs = lhs * rhs
      }
    }
    
    // 4
    extension Vector: CustomStringConvertible {
      var description: String {
        return "(\(x) \(y))"
      }
    }
    

    简单解释一下这段代码:

    1. Vector 声明成员 xyinit(_:_:)
    2. 使 Vector 遵从ExpressibleByIntegerLiteral 实现 init(integerLiteral:) 方法;
    3. 遵从 NumericVector 定义 magnitude,声明 init(exactly:) 并实现 +(lhs:rhs:),+=(lhs:rhs:),-(lhs:rhs:),-=(lhs:rhs:),*(lhs:rhs:),*=(lhs:rhs:)
    4. 实现 description 以遵从 CustomStringConvertible 协议。

    上述代码能让你更简易地操作向量:

    var first = Vector(1, 2) // (1,2)
    let second = Vector(3, 4) // (3,4)
    let third = first + second // (4,6)
    first += second // (4,6)
    let fourth = first - second // (1,2)
    first -= second // (1,2)
    

    因为不能定义 2 维向量的叉积,Swift 5 为此实现了 AdditiveArithmetic[ SE-0233 ]。它不需要遵从 ExpressibleByIntegerLiteral

    extension Vector: AdditiveArithmetic {
      static var zero: Vector {
        return Vector(0, 0)
      }
      
      static func +(lhs: Vector, rhs: Vector) -> Vector {
        return Vector(lhs.x + rhs.x, lhs.y + rhs.y)
      }
      
      static func +=(lhs: inout Vector, rhs: Vector) {
        lhs = lhs + rhs
      }
      
      static func -(lhs: Vector, rhs: Vector) -> Vector {
        return Vector(lhs.x - rhs.x, lhs.y - rhs.y)
      }
      
      static func -=(lhs: inout Vector, rhs: Vector) {
        lhs = lhs - rhs
      }
    }
    

    在这段代码中,通过定义zero 属性和实现 +(lhs:rhs:), +=(lhs:rhs:), -(lhs:rhs:), -=(lhs:rhs:) 运算符使 Vector 遵从 AdditiveArithmetic 协议。

    未完待续……


    原文:https://www.raywenderlich.com/55728-what-s-new-in-swift-5
    作者:Cosmin Pupăză
    编译:码王爷

    相关文章

      网友评论

          本文标题:Swift 5 新增了什么?(上)

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