Swift 5 系列 新特性

Swift 的每次更新都使得写起代码越来越简单高效。

截止目前,Swift 更新了 5.0 及 5.1:

  • Swift 5.0

  • Swift 5.1

一、Swift 5.0 新特性

1.1 Raw String

使用 # 包裹字符串

在字符串中包含 " 时不必再加 \

//before
let rain = "The \"rain\" in \"Spain\" falls mainly on the Spaniards." 
//after
let rain = #"The "rain" in "Spain" falls mainly on the Spaniards."#  

包含 \ 反斜杠也不需要再加转义符

// before
let keypaths = "Swift keypaths such as \\Person.name hold uninvoked references to properties."
// after
let keypaths = #"Swift keypaths such as \Person.name hold uninvoked references to properti

由于反斜杠作为字符串中的字符,所以在插入值的时候需要在后面再加个 #

  //before
 let answer = 42
 let dontpanic = "The answer to life, the universe, and everything is \(answer)"
 // after
 let answer = 42
 let dontpanic = #"The answer to life, the universe, and everything is \#(answer)"#

当字符串包含 # 时, 前后应用 ## 包裹字符串

let str = ##"My dog said "woof"#gooddog"##

用 #""" 开头 """#结尾 来表示多行字符串

  let multiline = #"""
  The answer to life,
  the universe,
  and everything is \#(answer).
  """#

由于不用反斜杠转义 使得正则表达式更加简洁明了

 //before
 let regex1 = "\\\\[A-Z]+[A-Za-z]+\\.[a-z]+"
//after
 let regex2 = #"\\[A-Z]+[A-Za-z]+\.[a-z]+"#

1.2 检查整数是否为偶数(Checking for integer muliples)

isMultiple(of:) 来检测一个数是否是另一个数的倍数

let rowNumber = 4

    /*相当于 if rowNumber %  2 == 0
      改成方法调用更易懂,更方便调用(有代码补全)
     */
    if rowNumber.isMultiple(of:2) {
    print("Even")
} else {
    print("Odd")
}

来检查整数是否为偶数 和 if rowNumber % 2 == 0 效果一样。

1.3 处理未来的枚举值(Handling future enum cases)

在枚举新增加一个 @unknown 修饰 default 分支,这样使得将来 enum 再增加一个 case 的时候,编译器会在该枚举的 switch 代码里生成一个警告。

  enum PasswordError: Error {
          case short
          case obvious
          case simple
    }

    func showOld(error: PasswordError){
             switch error {
             case .short:
                      print("Your password was too short.")
             case .obvious:
                      print("Your password was too obvious.")
             default:
                      print("Your password was too simple.")
             }
    }

上面代码假如我们再加个 case old,执行代码时它会自动进入到 default 分支,可这并不是我们想要的结果,因为这个密码是一个旧密码而不是密码太简单,这时候可以用 @unknown,如下:

   enum PasswordError: Error {
            case short
            case obvious
            case simple
            case old
   }

   func showNew(error: PasswordError) {
        switch error {
        case .short:
            print("Your password was too short.")
        case .obvious:
            print("Your password was too obvious.")
        @unknown default:
            print("Your password wasn't suitable.")
        }
    }  

这时会产生一个警告 ,因为新增了 case old ,switch 没有明确地处理每一个分支。

1.4 带条件的计数(Counting matching items in a sequence)

Sequence 新增加了一个方法 count(where:) 相当于 filter() + count 的结果,但是它更加简洁一步到位。

//before
let scores = [100,80,85]
let passCount = scores.filter{($0 >= 85)}.count

//after 避免生成一个数组
let scores = [100,80,85]
let passCount = scores.count { $0 >= 85}

这个方法适用于遵循了 Sequence 的所有类型,所以也可以用在 集合 和 字典 里。

1.5 字典 compactMapValues() 方法

为字典新增了一个方法 compactMapValues(),正如数组的 compactMap 函数一样,可以过滤 nil,类型转换。

 let times = [
        "Hudson": "38",
        "Clarke": "42",
        "Robinson": "35",
        "Hartis": "DNF"
    ]
    //通过 compactMapValues 将值转换成 integer 类型,并且将 DNF 过滤掉
     let finishers1 = times.compactMapValues { Int($0) }
    //也可以这样写
     let finishers2 = times.compactMapValues(Int.init)

     // 过滤 nil
     let people = [
        "Paul": 38,
        "Sophie": 8,
        "Charlotte": 5,
        "William": nil
      ]
     let knownAges = people.compactMapValues { $0 }

1.6 使用新的 Unicode 标量属性

在之前,可以为 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 5.1 新特性

2.1 结构体初始化

以前需要这样初始化结构体

// Synthesized default values for the memberwise initializer
// Proposal and implementation by an open source contributor Alejandro Alonso
// Swift Evolution: SE-0242
struct Dog {
 var name = "Generic dog name"
 var age = 0
}
let boltNewborn = Dog()
let daisyNewborn = Dog(name: "Daisy", age: 0) 
let benjiNewborn = Dog(name: "Benji") // 编译错误

现在最后一句也能编译通过了。

2.2 创建未初始化数组

创建未初始化的数组:

// 1
let randomSwitches = Array<String>(unsafeUninitializedCapacity: 5) {
  buffer, count in
  // 2
  for i in 0..<5 {
    buffer[i] = Bool.random() ? "on" : "off"
  }
  // 3
  count = 5
}

守则:

  • 使用 init(unsafeUninitializedCapacity:initializingWith:) 创建具有特定初始容量的随机开关
  • 循环通过随机开关,并使用 random() 设置每个开关状态
  • 为随机开关设置初始化元素的数量

参多特性请参考以下链接。