改进自动生成的struct
初始化方法
swift早期版本中支持了自动生成struct
的初始化方法
struct User {
var name: String
var loginCount: Int = 0
}
let piper = User(name: "Piper Chapman", loginCount: 0)
Swift 5.1改进了初始化方法,如果属性有了初始值,初始化的时候可以省略。
代码语言:javascript复制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 }
上面两个是等价的。
实际上下面这么写也一样
代码语言:javascript复制func double(_ number: Int) -> Int {
number * 2
}
更通用的Self
Swift 5.1中扩展了Self
的使用,在class
,struct
, enmu
中使用时候可以指向这个类型。这点在动态类型中很有用,特别是某些类型需要在运行时才能决定时候。
例如,下面场景中
代码语言:javascript复制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中一样工作。
不透明返回类型(Opaque Return types)
提案SE-0244为Swift 5.1带来了不透明类型:知晓某个对象的能力但是不需要知道这个对象的具体类型。
初看之下,它很像协议protocol,但不透明返回类型走的比这更远,它可以和associated type使用。
代码语言:javascript复制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")
- 类型实例之前就可以自定义下标
- swift 5.1中类型也可以定制
static
或者class
的下标了。
static
或者class
都是静态的前缀,区别是,class容许子类型覆盖
告警有歧义的none
Swift的可选(optional)是现实是通过有两个值some
和none
的enum来实现的。这样就和我们自己代码中有none
case的enum包裹在optional时候产生混淆。
enum BorderStyle {
case none
case solid(thickness: Int)
}
如果在非可选值中使用
代码语言:javascript复制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?”,提示你作修复。
匹配可选(optional)和非可选的(non-optional)的enmu
Swift一直能够在switch case聪明的处理可选(optional)和非可选的(non-optional)的string,和integer。到了Swift 5.1 也支持enum了。
代码语言:javascript复制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)
来判断。
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的版本中创建一个没有初始值的数组。
代码语言:javascript复制// 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:)
来创建一个初始大小的数组 - 循环该数组(randomSwitches), 给每个值设值。
- 第二个
inout
参数可以让你重新为数组设置长度。
总结
这些就是swift 5.1的更新了,不算难懂,确实让swift越来越好用。