SWIFT 5.1如今已經正式發佈,儘管它只是一個小版本,但它包含了大量的更改和改進-從基本的新特性,好比模塊穩定性(它使sdk供應商可以發佈預編譯的SWIFT框架),到全部的爲SwiftUI提供動力的新語法特性,甚至更遠的地方。算法
除了它的新功能外,SWIFT 5.1還包含了一些更小但仍然很是重要的新功能和改進。這種變化一開始看起來很小,甚至沒有必要,但最終會對咱們編寫和構造SWIFT代碼的方式產生至關大的影響。本週,讓咱們來看看其中的五個特性,以及它們在哪些狀況下可能有用。數據庫
在SWIFT中,使結構如此吸引人的許多因素之一是它們的自動生成。「成員」初始化器-它使咱們可以簡單地經過傳遞與其每一個屬性對應的值來初始化任何結構(不包含私有存儲的屬性),以下所示:編程
struct Message {
var subject: String
var body: String
}
let message = Message(subject: "Hello", body: "From Swift")
複製代碼
這些合成的初始化器在SWIFT 5.1中獲得了顯著改進,由於它們如今考慮到了默認屬性值,並自動將這些值轉換爲默認的初始化器參數。swift
假設咱們想要擴展上面的內容Message結構,支持附件,但咱們但願默認值爲空數組-同時,咱們還但願啓用Message初始化,而沒必要指定body所以,咱們還將給該屬性一個默認值:api
struct Message {
var subject: String
var body = ""
var attachments: [Attachment] = []
}
複製代碼
在SWIFT5.1和更早版本中,咱們仍然必須爲上述全部屬性傳遞初始化參數,無論它們是否有默認值。可是,在SWIFT5.1中,狀況再也不是這樣-這意味着咱們如今能夠初始化Message僅經過一個subject,就像這樣:數組
var message = Message(subject: "Hello, world!")
複製代碼
這真的很酷,它使得使用structs比之前更加方便。但也許更酷的是,就像使用標準默認參數時同樣,咱們仍然能夠經過傳遞一個參數來覆蓋任何默認的屬性值-這給了咱們很大的靈活性:安全
var message = Message(
subject: "Hello, world!",
body: "Swift 5.1 is such a great update!"
)
複製代碼
然而,雖然在應用程序或模塊中成員初始化程序很是有用,但它們仍然沒有做爲模塊的公共API的一部分公開-這意味着若是咱們構建某種形式的庫或框架,咱們仍然必須手動定義面向公共的初始化器(目前是這樣)。bash
斯威夫特Self關鍵詞(或者類型,真的)之前使咱們可以在不知道實際具體類型的上下文中動態引用一種類型-例如,經過在協議擴展中引用協議的實現類型:框架
extension Numeric {
func incremented(by value: Self = 1) -> Self {
return self + value
}
}
複製代碼
儘管這仍然是可能的,可是Self如今已經擴展到包括具體的類型-例如枚舉、結構和類-使咱們可以使用。Self做爲一種別名,指方法或屬性的圍封類型,像這樣:ide
extension TextTransform {
static var capitalize: Self {
return TextTransform { $0.capitalized }
}
static var removeLetters: Self {
return TextTransform { $0.filter { !$0.isLetter } }
}
}
複製代碼
咱們如今能夠用Self上面,而不是所有TextTransform類型名稱,固然是純粹的句法糖-可是它能夠幫助咱們的代碼更緊湊一點,特別是在處理長類型名稱時。咱們甚至能夠用Self還能夠在方法或屬性中內聯,從而使上述代碼更加緊湊:
extension TextTransform {
static var capitalize: Self {
return Self { $0.capitalized }
}
static var removeLetters: Self {
return Self { $0.filter { !$0.isLetter } }
}
}
複製代碼
除了引用一個封閉類型自己,咱們如今也可使用Self訪問實例方法或屬性中的靜態成員-在咱們但願在一個類型的全部實例中重用相同值的狀況下,這是很是有用的,例如cellReuseIdentifier在本例中:
class ListViewController: UITableViewController {
static let cellReuseIdentifier = "list-cell"
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(
ListTableViewCell.self,
forCellReuseIdentifier: Self.cellReuseIdentifier
)
}
}
複製代碼
再說一次,咱們能夠簡單地打出來ListViewController當訪問咱們的靜態屬性時,可是使用Self確實能夠提升代碼的可讀性,而且還可使咱們從新命名視圖控制器,而沒必要更新訪問其靜態成員的方式。
接下來,讓咱們看一下SWIFT 5.1如何使在選項上執行模式匹配變得更容易,這在打開可選值時確實很方便。例如,假設咱們正在開發一個包含Song模型-它有一個downloadState屬性,該屬性容許咱們跟蹤歌曲是否已被下載,若是當前正在下載歌曲,依此類推:
struct Song {
...
var downloadState: DownloadState?
}
複製代碼
上面的屬性是可選的,緣由是咱們想要nil表示缺乏下載狀態,也就是說,若是一首歌根本沒有下載。
就像咱們看了看「SWIFT中的模式匹配」,SWIFT的高級模式匹配功能使咱們可以直接打開可選值-而沒必要先打開它-可是,在SWIFT 5.1以前,這樣作要求咱們在每一個匹配案例中附加一個問號,以下所示:
func songDownloadStateDidChange(_ song: Song) {
switch song.downloadState {
case .downloadInProgress?:
showProgressIndiator(for: song)
case .downloadFailed(let error)?:
showDownloadError(error, for: song)
case .downloaded?:
downloadDidFinish(for: song)
case nil:
break
}
}
複製代碼
在SWIFT 5.1中,再也不須要那些尾隨問號,咱們如今能夠直接引用每一種狀況-就像打開非可選值時同樣:
func songDownloadStateDidChange(_ song: Song) {
switch song.downloadState {
case .downloadInProgress:
showProgressIndiator(for: song)
case .downloadFailed(let error):
showDownloadError(error, for: song)
case .downloaded:
downloadDidFinish(for: song)
case nil:
break
}
}
複製代碼
雖然在進一步減小實現公共模式所需的語法方面,上述變化是值得歡迎的,但它確實帶來了輕微的反作用,這可能會破壞某些枚舉和開關語句的源代碼。
由於SWIFT選項是使用Optional引擎蓋下的枚舉,咱們再也不可以在包含如下兩個元素的任何枚舉上執行上述類型的可選模式匹配。some或none案件-由於這些案件如今將與下列案件發生衝突Optional包含。
然而,能夠說,任何包含此類案例的枚舉(特別是none),應該使用可選的方法來實現-由於表示潛在缺失的值本質上就是選項符所作的。
最初是做爲SwiftUI初始版本的一部分引入的,Identifiable協議如今已經進入SWIFT標準庫,並提供了一種簡單和統一的方法來標記任何類型爲具備穩定、惟一的標識符。
爲了符合這個新協議,咱們只需聲明一個id屬性,該屬性能夠包含任何Hashable類型-例如String:
struct User: Identifiable {
typealias ID = String
var id: ID
var name: String
}
複製代碼
相似於什麼時候Result做爲SWIFT5.0的一部分添加到標準庫中,這是如今擁有Identifable任何SWIFT模塊均可以使用它在不一樣的代碼基礎上共享需求。
例如,使用受限的協議擴展,咱們能夠添加一個方便的API來轉換Sequence它在字典中包含可識別的元素,而後將該擴展做爲庫的一部分進行銷售,而不須要咱們本身定義任何協議:
public extension Sequence where Element: Identifiable {
func keyedByID() -> [Element.ID : Element] {
var dictionary = [Element.ID : Element]()
forEach { dictionary[$0.id] = $0 }
return dictionary
}
}
複製代碼
上面的api被實現爲一個方法,而不是一個計算屬性,由於它的時間複雜度是O(N)..有關在方法和計算屬性之間選擇的更多信息,請參見這篇文章.
然而,當標準庫的新Identifiable協議在處理每一個值都有一個穩定的標識符的集合時很是有用,它對提升代碼的實際類型安全性沒有多大做用。
由於這一切Identifiable是否要求咱們定義任何可持續的id屬性時,它不會保護咱們避免不當心混淆標識符-例如在這種狀況下,當咱們錯誤地傳遞User函數的ID,該函數接受Video身份證:
postComment(comment, onVideoWithID: user.id)
複製代碼
因此仍然有不少強有力的用例Identifier類型和更健壯Identifiable協議-好比咱們看過的那些「SWIFT中的類型安全標識符」,這就防止了上述錯誤的發生。不過,如今仍是很高興一些的版本Identifiable協議在標準庫中,即便它是比較有限的。
最後,讓咱們看一個全新的標準庫API,它是SWIFT5.1順序集合差別的一部分。隨着咱們做爲一個社區,愈來愈接近聲明性編程的世界,使用諸如Comit和SwiftUI這樣的工具-可以計算兩種狀態之間的差別變得愈來愈重要。
畢竟,聲明性用戶界面開發就是不斷地呈現狀態的新快照,而Swiftui和新的不一樣數據源可能會作大部分的繁重工做來實現這一點-可以計算出兩種狀態之間的差別是很是有用的。
例如,假設咱們正在構建一個DatabaseController這樣咱們就能夠輕鬆地用內存模型數組更新磁盤上的數據庫。爲了可以肯定應該插入仍是刪除模型,咱們如今只需調用新的differenceAPI來計算舊數組和新數組之間的差別,而後迭代該diff中的更改,以執行數據庫操做:
class DatabaseController<Model: Hashable & Identifiable> {
private let database: Database
private(set) var models: [Model] = []
...
func update(with newModels: [Model]) {
let diff = newModels.difference(from: models)
for change in diff {
switch change {
case .insert(_, let model, _):
database.insert(model)
case .remove(_, let model, _):
database.delete(model)
}
}
models = newModels
}
}
複製代碼
可是,上面的實現不考慮移動模型,由於在默認狀況下,移動將被視爲單獨的插入和刪除。要解決這個問題,咱們還能夠調用inferringMoves方法時,而後查看每一個插入是否與刪除相關聯,若是是,則將其視爲移動,以下所示:
func update(with newModels: [Model]) {
let diff = newModels.difference(from: models).inferringMoves()
for change in diff {
switch change {
case .insert(let index, let model, let association):
// If the associated index isn't nil, that means // that the insert is associated with a removal, // and we can treat it as a move. if association != nil { database.move(model, toIndex: index) } else { database.insert(model) } case .remove(_, let model, let association): // We'll only process removals if the associated
// index is nil, since otherwise we will already
// have handled that operation as a move above.
if association == nil {
database.delete(model)
}
}
}
models = newModels
}
複製代碼
如今,Diffing被內置到標準庫(以及UIKit和AppKit)中,這是一個奇妙的消息-由於編寫一個高效、靈活和健壯的差分算法是很是困難的。
SWIFT5.1不只是SwiftUI和Combinding的關鍵推進者,對於任何銷售預編譯框架的團隊來講也是個大新聞,由於SWIFT如今不只是ABI穩定的,並且是模塊穩定的。除此以外,SWIFT5.1還包括許多小的但受歡迎的更改和調整,它們應該適用於幾乎全部的代碼庫-儘管在本文中咱們已經看了其中的五個,但在接下來的幾周和幾個月中,咱們將繼續深刻研究SWIFT5.1的更多方面。
本文中沒有包含任何與SwiftUI相關的特性,緣由是這些特性在「SWIFT 5.1的特性爲SwiftUI的API提供了動力」..靜態下標也是如此。「斯威夫特中下標的力量」.
你認爲如何?您是否已經將項目遷移到SWIFT5.1,若是是,您最喜歡的新功能是什麼?讓我知道-連同你可能有的任何問題、評論或反饋-或者統統過加咱們的交流羣 點擊此處進交流羣 ,來一塊兒交流或者發佈您的問題,意見或反饋。