1/4/2020, 10:59:43 PM
又是一篇迟来的文章。主要是译自: What’s new in Swift 5.1。
阅读之前可以查看我之前总结的Swift 3 到Swift 5的新特性。
Swift5.1支持了SwiftUI, 可以看出本次的更新更多了是为了更好的“声明”UI。
struct
初始化方法swift早期版本中支持了自动生成struct
的初始化方法
struct User { var name: String var loginCount: Int = 0 } let piper = User(name: "Piper Chapman", loginCount: 0)
Swift 5.1改进了初始化方法,如果属性有了初始值,初始化的时候可以省略。
let gloria = User(name: "Gloria Mendoza", loginCount: 0) let suzanne = User(name: "Suzanne Warren")
这点实际上很好理解,Swift 5.1中单行返回函数可以省略return
关键词。
let doubled1 = [1, 2, 3].map { $0 * 2 } let doubled2 = [1, 2, 3].map { return $0 * 2 }
上面两个是等价的。
实际上下面这么写也一样
func double(_ number: Int) -> Int { number * 2 }
Self
Swift 5.1中扩展了Self
的使用,在class
,struct
, enmu
中使用时候可以指向这个类型。这点在动态类型中很有用,特别是某些类型需要在运行时才能决定时候。
例如,下面场景中
class NetworkManager { class var maximumActiveRequests: Int { return 4 } func printDebugData() { print("Maximum network requests: \(NetworkManager.maximumActiveRequests).") } }
上面声明了静态的maximumActiveRequests
属性,并且使用实例方法printDebugData
来打印这个属性。现在这样工作的挺好。但是如果NetworkManager
有子类,情况就不一样了
class ThrottledNetworkManager: NetworkManager { override class var maximumActiveRequests: Int { return 1 } }
子类改变了maximumActiveRequests
,但是如果我们调用printDebugData()
,它只会打印父类的属性。
let manager = ThrottledNetworkManager() manager.printDebugData()
它理应打印1而不是4,这就是Self
要解决的问题。我们改写printDebugData()
用Self
(大写S)来指向当前的类型:
class ImprovedNetworkManager { class var maximumActiveRequests: Int { return 4 } func printDebugData() { print("Maximum network requests: \(Self.maximumActiveRequests).") } }
Self
在协议中仍然像早期Swift中一样工作。
提案SE-0244为Swift 5.1带来了不透明类型:知晓某个对象的能力但是不需要知道这个对象的具体类型。
初看之下,它很像协议protocol,但不透明返回类型走的比这更远,它可以和associated type使用。
protocol Fighter { } struct XWing: Fighter { } func launchFighter() -> Fighter { return XWing() } let red5 = launchFighter()
上面launchFighter
返回某种Fighter,但是不知道具体那个类型,它可以是XWing,或者我们新增一个strunct YWing:Fighter {}
。
这样写有个问题,如果你想知道red5
具体是那种类型的飞机呢?你可能想到方案是,让Fighter
遵循Equatable
协议,然后我们就可以使用==
方法。但是实际使用的时候会发现下面的报错:
这是因为Equatable
有一个Self
的associated type。 有associated type的协议看起来像类型,但是它们实际上不是,它们实际上表示的是“遵循此协议的任意类型”
Swift 5.1中的不透明返回类型,可以将这种协议作做一个普通的类型来使用。只需要在协议名前增加some
关键词。
func launchOpaqueFighter() -> some Fighter { return XWing() }
不透明返回类型(Opaque Return types)可以带来的好处有:
Self
或者associated type
,因为编译器会明确知道内部具体的类型。Static
和class
类下标(subscripts)静态Static
类型的属性和方法,可用来在类型所有实例间共享某些值。例如你可以在你的App中集中管理配置。
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
。
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")
static
或者class
的下标了。static
或者class
都是静态的前缀,区别是,class容许子类型覆盖
none
Swift的可选(optional)是现实是通过有两个值some
和none
的enum来实现的。这样就和我们自己代码中有none
case的enum包裹在optional时候产生混淆。
enum BorderStyle { case none case solid(thickness: Int) }
如果在非可选值中使用
let border1: BorderStyle = .none print(border1)
上面会打印none
。 但是如果我们在使用在可选值中,我们不知道什么边框时候,Swift 5.1之前的版本会有问题。
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?”,提示你作修复。
Swift一直能够在switch case聪明的处理可选(optional)和非可选的(non-optional)的string,和integer。到了Swift 5.1 也支持enum了。
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…“了。
Swift 5.1 为可排序集合(内含Equatable
元素)提供了一个difference(from:)
方法来计算两个集合,那个元素被移除了,新增了哪个……
此方法被标注为,swift 5.1才@available
,因此使用需要if swift(>=5.1)
来判断。
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哈~~
可以在Swfift 5.1的版本中创建一个没有初始值的数组。
// 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:)
来创建一个初始大小的数组inout
参数可以让你重新为数组设置长度。这些就是swift 5.1的更新了,不算难懂,确实让swift越来越好用。😄