在 7 月 29 日的發(fā)布的 Xcode 11 beta 5 中鳄抒,包含了 Swift 5.1闯捎。如果想要體驗(yàn)這些新特性,需要至少安裝好這個(gè)版本的 Xcode许溅。本文內(nèi)容主要參考 Raywenderlich 的這篇文章 編寫瓤鼻,如果想要查看原文,請(qǐng)點(diǎn)擊鏈接查看贤重。
Swift 5.1 在 5.0 引入的 ABI 穩(wěn)定性基礎(chǔ)上增加了模塊穩(wěn)定性茬祷。雖然 ABI 穩(wěn)定性在運(yùn)行時(shí)考慮到應(yīng)用程序的兼容性,但模塊穩(wěn)定性在編譯時(shí)支持庫的兼容性并蝗。這意味著你可以在任何編譯器版本中使用第三方框架祭犯,而不只是構(gòu)建它的那個(gè)版本。
下面我們一起看一下有哪些改進(jìn)滚停。
模糊的結(jié)果類型 (Opaque Result Types)
在開發(fā)的時(shí)候沃粗,有時(shí)候可能會(huì)使用 protocol 作為返回值類型。下面來看一個(gè)例子:
protocol BlogPost {
var title: String { get }
var author: String { get }
}
struct Tutorial: BlogPost {
let title: String
let author: String
}
func createBlogPost(title: String, author: String) -> BlogPost {
guard !title.isEmpty && !author.isEmpty else {
fatalError("No title and/or author assigned!")
}
return Tutorial(title: title, author: author)
}
let swift4Tutorial = createBlogPost(title: "What's new in Swift 4.2?",
author: "Cosmin Pup?z?")
let swift5Tutorial = createBlogPost(title: "What's new in Swift 5?",
author: "Cosmin Pup?z?")
上面代碼:1)首先定義 BlogPost
協(xié)議铐刘;2)定義 Tutorial
并實(shí)現(xiàn) BlogPost
協(xié)議;3)定義 createBlogPost()
方法用于創(chuàng)建 BlogPost
影晓;4)用 createBlogPost()
創(chuàng)建 swift4Tutorial
和 swift5Tutorial
兩個(gè)實(shí)例镰吵。
下面我們想比較 swift4Tutorial
和 swift5Tutorial
是否相等:
// 錯(cuò)誤:Binary operator '==' cannot be applied to two 'BlogPost' operands
let isSameTutorial = (swift4Tutorial == swift5Tutorial)
因?yàn)?BlogPost
還沒有實(shí)現(xiàn) Equatable
檩禾,所以會(huì)出錯(cuò)。下面讓 BlogPost
繼承自 Equatable
:
protocol BlogPost: Equatable {
var title: String { get }
var author: String { get }
}
這時(shí)另一個(gè)錯(cuò)誤出現(xiàn)在 createBlogPost()
方法:
// Protocol 'BlogPost' can only be used as a generic constraint because it has Self or associated type requirements
func createBlogPost(title: String, author: String) -> BlogPost {
guard !title.isEmpty && !author.isEmpty else {
fatalError("No title and/or author assigned!")
}
return Tutorial(title: title, author: author)
}
這個(gè)錯(cuò)誤的意思是 BlogPost
只能用來作為泛型約束疤祭,因?yàn)樗?Self
或者有關(guān)聯(lián)類型要求盼产。查看 Equatable
的定義勺馆,確實(shí)是有 Self
:
public protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
具有關(guān)聯(lián)類型的協(xié)議不是類型戏售,即使他們看起來是類型。而它們更像是類型占位符草穆,這個(gè)類型可以是任何實(shí)現(xiàn)了我這個(gè)協(xié)議的類型灌灾。
在 Swift 5.1 中,我們就可以在返回值類型前面加上 some
來解決這個(gè)問題悲柱。把 createBlogPost()
方法改為:
func createBlogPost(title: String, author: String) -> some BlogPost {
guard !title.isEmpty && !author.isEmpty else {
fatalError("No title and/or author assigned!")
}
return Tutorial(title: title, author: author)
}
some
的作用就是告訴編譯器我這個(gè)方法的返回值可以是實(shí)現(xiàn)了 BlogPost
的任何類型锋喜。
修改完成之后,我們直接用 ==
比較 swift4Tutorial
和 swift5Tutorial
就不會(huì)報(bào)錯(cuò)了豌鸡。
在 SwiftUI 中嘿般,就是使用了 some
:
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
隱式返回
在 Swift 5.1 中,如果方法體只有一行語句涯冠,則可以省略 return
:
func myName() -> String {
"Lebron"
}
屬性包裝器
在 Swift 5.1 之前炉奴,使用計(jì)算屬性時(shí),可能出現(xiàn)類似下面的代碼:
var settings = ["swift": true, "latestVersion": true]
struct Settings {
var isSwift: Bool {
get {
return settings["swift"] ?? false
}
set {
settings["swift"] = newValue
}
}
var isLatestVersion: Bool {
get {
return settings["latestVersion"] ?? false
}
set {
settings["latestVersion"] = newValue
}
}
}
var newSettings = Settings()
newSettings.isSwift
newSettings.isLatestVersion
newSettings.isSwift = false
newSettings.isLatestVersion = false
上面的代碼中蛇更,如果有更多的計(jì)算屬性瞻赶,那么就要寫跟多的重復(fù)代碼。為了解決這個(gè)問題械荷,Swift 5.1 引入了屬性包裝器共耍,可以把上面的代碼簡(jiǎn)寫為:
var settings = ["swift": true, "latestVersion": true]
@propertyWrapper
struct SettingsWrapper {
let key: String
let defaultValue: Bool
var wrappedValue: Bool {
get {
settings[key] ?? defaultValue
}
set {
settings[key] = newValue
}
}
}
struct Settings {
@SettingsWrapper(key: "swift", defaultValue: false) var isSwift: Bool
@SettingsWrapper(key: "latestVersion", defaultValue: false) var isLatestVersion: Bool
}
-
@propertyWrapper
把SettingsWrapper
標(biāo)記為屬性包裝器。作為一個(gè)屬性包裝器吨瞎,必須有一個(gè)名為wrappedValue
的屬性痹兜。 - 使用
@SettingsWrapper
標(biāo)記Settings
中對(duì)應(yīng)的屬性。
在 struct 中定義屬性的默認(rèn)值
在 Swift 5.1 前颤诀,如果想要給 struct 的屬性定義默認(rèn)值字旭,必須這么寫:
struct Author {
let name: String
var tutorialCount: Int
init(name: String, tutorialCount: Int = 0) {
self.name = name
self.tutorialCount = tutorialCount
}
}
let author = Author(name: "George")
而在 Swift 5.1 以后,可以直接像 class 那樣給屬性定義默認(rèn)值:
struct Author {
let name: String
var tutorialCount = 0
}
使用 Self
調(diào)用靜態(tài)成員
在 Swift 5.1 以前崖叫,需要使用 類名.靜態(tài)成員
來調(diào)用靜態(tài)成員:
struct Editor {
static func reviewGuidelines() {
print("Review editing guidelines.")
}
func edit() {
Editor.reviewGuidelines()
print("Ready for editing!")
}
}
而在 Swift 5.1 中遗淳,可以直接用 Self.靜態(tài)成員
:
struct Editor {
static func reviewGuidelines() {
print("Review editing guidelines.")
}
func edit() {
Self.reviewGuidelines()
print("Ready for editing!")
}
}
創(chuàng)建未初始化的數(shù)組
Swift 5.1 給 Array
添加了一個(gè)新的初始化方法:init(unsafeUninitializedCapacity:initializingWith:)
let randomSwitches = Array<String>(unsafeUninitializedCapacity: 5) { buffer, count in
for i in 0..<5 {
buffer[i] = Bool.random() ? "on" : "off"
}
// 必須給 `count` 賦值,否則 `randomSwitches` 會(huì)變成空數(shù)組
count = 5
}
static
和 class
下標(biāo)
在 Swift 5.1 中可以定義 static
和 class
下標(biāo):
@dynamicMemberLookup
class File {
let name: String
init(name: String) {
self.name = name
}
// 定義 static 下標(biāo)
static subscript(key: String) -> String {
switch key {
case "path":
return "custom path"
default:
return "default path"
}
}
// 使用 Dynamic Member Lookup 重寫上面的下標(biāo)
class subscript(dynamicMember key: String) -> String {
switch key {
case "path":
return "custom path"
default:
return "default path"
}
}
}
File["path"] // "custom path"
File["PATH"] // "default path"
File.path // "custom path"
File.PATH // "default path"
用 @dynamicMemberLookup
標(biāo)記 File
是為了可以使用點(diǎn)語法來訪問自定義的下標(biāo)心傀。
Keypath 支持動(dòng)態(tài)成員查找
struct Point {
let x, y: Int
}
@dynamicMemberLookup
struct Circle<T> {
let center: T
let radius: Int
// 定義泛型下標(biāo)屈暗,可以用 keypath 來訪問 `center` 的屬性
subscript<U>(dynamicMember keyPath: KeyPath<T, U>) -> U {
center[keyPath: keyPath]
}
}
let center = Point(x: 1, y: 2)
let circle = Circle(center: center, radius: 1)
circle.x // 1
circle.y // 2
Tuple 支持 Keypath
struct Instrument {
let brand: String
let year: Int
let details: (type: String, pitch: String)
}
let instrument = Instrument(
brand: "Roland",
year: 2019,
details: (type: "acoustic", pitch: "C")
)
// 使用 keypath 訪問 tuple
let type = instrument[keyPath: \Instrument.details.type]
let pitch = instrument[keyPath: \Instrument.details.pitch]
weak
和 unowned
屬性自動(dòng)實(shí)現(xiàn) Equatable
和 Hashable
class Key {
let note: String
init(note: String) {
self.note = note
}
}
extension Key: Hashable {
static func == (lhs: Key, rhs: Key) -> Bool {
lhs.note == rhs.note
}
func hash(into hasher: inout Hasher) {
hasher.combine(note)
}
}
class Chord {
let note: String
init(note: String) {
self.note = note
}
}
extension Chord: Hashable {
static func == (lhs: Chord, rhs: Chord) -> Bool {
lhs.note == rhs.note
}
func hash(into hasher: inout Hasher) {
hasher.combine(note)
}
}
struct Tune: Hashable {
unowned let key: Key
weak var chord: Chord?
}
在 Swift 5.1 以前,Tune
的定義里面會(huì)報(bào)錯(cuò):沒有實(shí)現(xiàn) Equatable
和 Hashable
;在 Swift 5.1 則已經(jīng)自動(dòng)實(shí)現(xiàn)养叛。
不明確的枚舉 case
如果有不明確的枚舉 case种呐,在 Swift 5.1 中會(huì)產(chǎn)生警告??。
enum TutorialStyle {
case cookbook, stepByStep, none
}
// 會(huì)產(chǎn)生警告
let style: TutorialStyle? = .none
因?yàn)?style
是 Optional
類型弃甥,編譯器不知道 .none
是 Optional.none
還是 TutorialStyle.none
爽室,所有要寫具體一點(diǎn),例如:let style: TutorialStyle? = TutorialStyle.none
匹配可選類型的枚舉
在 Swift 5.1 以前淆攻,switch
語句中匹配可選類型的枚舉時(shí)阔墩,case 后面需要加問號(hào):
enum TutorialStatus {
case written, edited, published
}
let status: TutorialStatus? = .published
switch status {
case .written?:
print("Ready for editing!")
case .edited?:
print("Ready to publish!")
case .published?:
print("Live!")
case .none:
break
}
而在 Swift 5.1 中,可以把問號(hào)去掉:
switch status {
case .written:
print("Ready for editing!")
case .edited:
print("Ready to publish!")
case .published:
print("Live!")
case .none:
break
}
Tuple 類型的轉(zhuǎn)換
let temperatures: (Int, Int) = (25, 30)
let convertedTemperatures: (Int?, Any) = temperatures
在 Swift 5.1 以前瓶珊,(Int, Int)
是不能轉(zhuǎn)換成 (Int?, Any)
的啸箫,而在 Swift 5.1 可以。
Any 和泛型參數(shù)的方法重載
func showInfo(_: Any) -> String {
return "Any value"
}
func showInfo<T>(_: T) -> String {
return "Generic value"
}
showInfo("Swift")
在 Swift 5.1 以前艰毒,showInfo("Swift")
返回的是 Any value
筐高;而在 Swift 5.1 中,返回的是 Generic value
丑瞧。也就是說在 Swift 5.1 以前柑土,Any 參數(shù)類型的方法優(yōu)先;而在 Swift 5.1 中绊汹,泛型參數(shù)的方法優(yōu)先稽屏。
autoclosure 參數(shù)可以定義別名
在 Swift 5.1 中,@autoclosure
標(biāo)記的 closure 參數(shù)可以使用別名:
struct Closure<T> {
typealias ClosureType = () -> T
func apply(closure: @autoclosure ClosureType) {
closure()
}
}
在 Swift 5.1 以前西乖,只能這樣寫:
struct Closure<T> {
func apply(closure: @autoclosure () -> T) {
closure()
}
}
在 Objective-C 方法中返回 self
在 Swift 5.1 以前狐榔,被 @objc
標(biāo)記的方法中返回 self
,必須繼承自 NSObject
:
class Clone: NSObject {
@objc func clone() -> Self {
return self
}
}
在 Swift 5.1 中获雕,則無需集成 NSObject
class Clone {
@objc func clone() -> Self {
self
}
}
完
想及時(shí)看到我的新文章的薄腻,可以關(guān)注我。同時(shí)也歡迎加入我管理的Swift開發(fā)群:536353151
届案。