---
title: "Swift 5.1"
description: "Swift5.1支持了SwiftUI, 可以看出本次的更新更多了是为了更好的“声明”UI"
url: "https://hicc.pro/p/swift/whats-new-in-swift-5-1"
author: "tyler wang"
published: "2020-01-04T14:59:43.000Z"
updated: "2021-07-03T09:01:52.000Z"
category: "Swift"
tags:
  - "Swift"
  - "Swift 5.1"
image: "https://image.hicc.pro/pro/20210703170120FhCNIndb0hECdjWpXEXNyGfJ8IfREl9R9WidOVP-YoltTw2Yw.jpg"
lang: "zh-CN"
site: "Hipo日志"
---

# Swift 5.1

又是一篇迟来的文章。主要是译自： [What’s new in Swift 5.1](https://www.hackingwithswift.com/articles/182/whats-new-in-swift-5-1)。

*阅读之前可以查看我之前总结的[Swift 3 到Swift 5的新特性](/whats-new-in-swift-3-to-5-1/)。*

Swift5.1支持了SwiftUI, 可以看出本次的更新更多了是为了更好的“声明”UI。

---

### 改进自动生成的`struct`初始化方法

swift早期版本中支持了自动生成`struct`的初始化方法

```swift
struct User {
    var name: String
    var loginCount: Int = 0
}

let piper = User(name: "Piper Chapman", loginCount: 0)
```

Swift 5.1改进了初始化方法，如果属性有了初始值，初始化的时候可以省略。

```swift
let gloria = User(name: "Gloria Mendoza", loginCount: 0)
let suzanne = User(name: "Suzanne Warren")
```

### 单行函数隐式返回

这点实际上很好理解，Swift 5.1中单行返回函数可以省略`return`关键词。

```swift
let doubled1 = [1, 2, 3].map { $0 * 2 }
let doubled2 = [1, 2, 3].map { return $0 * 2 }
```
上面两个是等价的。

实际上下面这么写也一样

```swift
func double(_ number: Int) -> Int {
    number * 2
}
```

### 更通用的`Self`

Swift 5.1中扩展了`Self`的使用，在`class`,`struct`, `enmu`中使用时候可以指向这个类型。这点在动态类型中很有用，特别是某些类型需要在运行时才能决定时候。

例如，下面场景中

```swift
class NetworkManager {
    class var maximumActiveRequests: Int {
        return 4
    }

    func printDebugData() {
        print("Maximum network requests: \(NetworkManager.maximumActiveRequests).")
    }
}
```

上面声明了静态的`maximumActiveRequests`属性，并且使用实例方法`printDebugData`来打印这个属性。现在这样工作的挺好。但是如果`NetworkManager`有子类，情况就不一样了

```swift
class ThrottledNetworkManager: NetworkManager {
    override class var maximumActiveRequests: Int {
        return 1
    }
}
```

子类改变了`maximumActiveRequests`，但是如果我们调用`printDebugData()`，它只会打印父类的属性。

```swift
let manager = ThrottledNetworkManager()
manager.printDebugData()
```

它理应打印1而不是4，这就是`Self`要解决的问题。我们改写`printDebugData()`用`Self`(大写S)来指向当前的类型:

```swift
class ImprovedNetworkManager {
    class var maximumActiveRequests: Int {
        return 4
    }

    func printDebugData() {
        print("Maximum network requests: \(Self.maximumActiveRequests).")
    }
}
```

`Self`在协议中仍然像早期Swift中一样工作。

### 不透明返回类型(Opaque Return types)

提案[SE-0244](https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md)为Swift 5.1带来了不透明类型：知晓某个对象的能力但是不需要知道这个对象的具体类型。

初看之下，它很像协议protocol，但不透明返回类型走的比这更远，它可以和associated type使用。

```swift
protocol Fighter { }
struct XWing: Fighter { }

func launchFighter() -> Fighter {
    return XWing()
}

let red5 = launchFighter()
```

上面`launchFighter`返回某种Fighter，但是不知道具体那个类型，它可以是XWing，或者我们新增一个`strunct YWing:Fighter {}`。

这样写有个问题，如果你想知道`red5`具体是那种类型的飞机呢？你可能想到方案是，让`Fighter`遵循`Equatable`协议，然后我们就可以使用`==`方法。但是实际使用的时候会发现下面的报错：

![“Protocol 'Fighter' can only be used as a generic constraint because it has Self or associated type requirements.”](https://image.hicc.pro/hicc_me1386836240_18E31C76-14C9-4F16-8A5D-488046B25AD0.png)

这是因为`Equatable`有一个`Self`的associated type。 有associated type的协议看起来像类型，但是它们实际上不是，它们实际上表示的是“遵循此协议的任意类型”

Swift 5.1中的不透明返回类型，可以将这种协议作做一个普通的类型来使用。只需要在协议名前增加`some`关键词。

```swift
func launchOpaqueFighter() -> some Fighter {
    return XWing()
}
```

不透明返回类型(Opaque Return types)可以带来的好处有：

- 我们的函数决定具体的返回类型，而不是函数的调用方。
- 我们不需要在关心`Self`或者`associated type`，因为编译器会明确知道内部具体的类型。
- 为函数定义方将来改变实现留有余地。
- 函数定义方不需要对外保留内部的类型。

### 支持`Static`和`class`类下标(subscripts)

静态`Static`类型的属性和方法，可用来在类型所有实例间共享某些值。例如你可以在你的App中集中管理配置。

```swift
public enum OldSettings {
    private static var values = [String: String]()

    static func get(_ name: String) -> String? {
        return values[name]
    }

    static func set(_ name: String, to newValue: String?) {
        print("Adjusting \(name) to \(newValue ?? "nil")")
        values[name] = newValue
    }
}

OldSettings.set("Captain", to: "Gary")
OldSettings.set("Friend", to: "Mooncake")
print(OldSettings.get("Captain") ?? "Unknown")
```

字典包裹在类型中可以让更小心的控制它，并且使用没有`case`的`enum`，也就让你没法实例化`Settings`。

在Swift 5.1中你可以使用`static subscript`。

```swift
public enum NewSettings {
    private static var values = [String: String]()

    public static subscript(_ name: String) -> String? {
        get {
            return values[name]
        }
        set {
            print("Adjusting \(name) to \(newValue ?? "nil")")
            values[name] = newValue
        }
    }
}

NewSettings["Captain"] = "Gary"
NewSettings["Friend"] = "Mooncake"
print(NewSettings["Captain"] ?? "Unknown")
```

- 类型实例之前就可以自定义下标
- swift 5.1中类型也可以定制`static`或者`class`的下标了。

*`static`或者`class`都是静态的前缀，区别是，class容许子类型覆盖*

![swift中`static`或者`class`方法的区别](https://image.hicc.pro/hicc_me-1378524030_B520F4BF-EBC1-4CF1-AE0D-54C1379DBE6A.png)

### 告警有歧义的`none`

Swift的可选(optional)是现实是通过有两个值`some`和`none`的enum来实现的。这样就和我们自己代码中有`none`case的enum包裹在optional时候产生混淆。

```swift
enum BorderStyle {
    case none
    case solid(thickness: Int)
}
```

如果在非可选值中使用

```swift
let border1: BorderStyle = .none
print(border1)
```
上面会打印`none`。 但是如果我们在使用在可选值中，我们不知道什么边框时候，Swift 5.1之前的版本会有问题。

```swift
let border2: BorderStyle? = .none
print(border2)
```
会打印`nil`, 因为swfit会默认将`.none`推导为可选是空值，而不是`BorderStyle.none`。

Swift 5.1中会对此做出警告：“Assuming you mean 'Optional.none'; did you mean 'BorderStyle.none' instead?”，提示你作修复。

![](https://image.hicc.pro/hicc_me-1513685327_8A900C29-9C9B-404A-B3ED-64B873151F9C.png)

### 匹配可选(optional)和非可选的(non-optional)的enmu

Swift一直能够在switch case聪明的处理可选(optional)和非可选的(non-optional)的string，和integer。到了Swift 5.1 也支持enum了。

```swift
enum BuildStatus {
    case starting
    case inProgress
    case complete
}

let status: BuildStatus? = .inProgress

switch status {
case .inProgress:
    print("Build is starting…")
case .complete:
    print("Build is complete!")
default:
    print("Some other build status")
}
```

现在能够正常的打印，”Build is starting…“了。

### 可排序集合的diff

Swift 5.1 为可排序集合(内含`Equatable`元素)提供了一个`difference(from:)`方法来计算两个集合，那个元素被移除了，新增了哪个……

*此方法被标注为，swift 5.1才`@available`,因此使用需要`if swift(>=5.1)`来判断*。

```swift
let operatingSystems = ["Yosemite",
                        "El Capitan",
                        "Sierra",
                        "High Sierra",
                        "Mojave",
                        "Catalina"]
var answers = ["Mojave",
               "High Sierra",
               "Sierra",
               "El Capitan",
               "Yosemite",
               "Mavericks"]

#if swift(>=5.1)
let differences = operatingSystems.difference(from: answers)
let sameAnswers = answers.applying(differences) ?? []
// 1
for change in differences.inferringMoves() {
  switch change {
    // 2
    case .insert(let offset, let element, let associatedWith):
      answers.insert(element, at: offset)
      guard let associatedWith = associatedWith else {
        print("\(element) inserted at position \(offset + 1).")
        break
      }
      print("""
            \(element) moved from position \(associatedWith + 1) to position
            \(offset + 1).
            """)
    // 3
    case .remove(let offset, let element, let associatedWith):
      answers.remove(at: offset)
      guard let associatedWith = associatedWith else {
        print("\(element) removed from position \(offset + 1).")
        break
      }
      print("""
            \(element) removed from position \(offset + 1) because it should be
              at position \(associatedWith + 1).
            """)
  }
}
#endif
```

- 使用`inferringMoves()`得到diff中所欲改变
- 通过`associatedWith`不为`nil`的`.insert(offset:element:associatedWith:)`来判断是插入的操作
- 通过`associatedWith`不为`nil`的`.remove(offset:element:associatedWith:)`来判断是删除的操作

这个功能确实非常的pro哈～～

### 创建没有初始值的数组(uninitialized arrays)

可以在Swfift 5.1的版本中创建一个没有初始值的数组。

```swift
// 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
}
```

1. 使用`init(unsafeUninitializedCapacity:initializingWith:)`来创建一个初始大小的数组
2. 循环该数组(randomSwitches), 给每个值设值。
3. 第二个`inout`参数可以让你重新为数组设置长度。

## 总结

这些就是swift 5.1的更新了，不算难懂，确实让swift越来越好用。😄

---

原文 / Source: <https://hicc.pro/p/swift/whats-new-in-swift-5-1>
