Swift的官方網站上的About頁面列出了三個關鍵字:
- 安全(Safe):為了最大限度地減少開發人員的錯誤;
- 迅速(Fast):執行的速度要快;
- 表現力(Expressive):因為Swift的目標是盡可能清晰易懂。
是什么使代碼 “Swifty”? —— Safe 介紹了如何有選擇地使用類型系統的各個方面和功能,以使我們的代碼更易于理解和使用。
是什么使代碼 “Swifty”? —— Expressive 介紹了如何使用表達性命名和API設計傳達我們的代碼意圖
性能之路(The path to performance)
Swift的第二個核心目標,就是要快一些,總的來說,這有點棘手。畢竟,編寫高性能代碼的主要部分在于測量,微調和再次測量。但是,使我們的代碼在性能方面與Swift本身更加一致的一種方法是,充分利用標準庫所提供的功能——特別是在處理集合(例如字符串)時。
就像我們在 Swift:字符串解析和Swift:集合切片中看過一樣,Swift標準庫針對性能進行了高度優化,并且使我們能夠以高效的方式執行許多常見的集合操作-假設我們使用正確的API。
例如,從字符串中刪除一組特定字符的一種常見方法是使用舊的ReplacementOccurences(of:with :)
API,該API是Swift的String
類型從其表親Objective-C的NSString
繼承而來的。在這里,我們使用了對該API的一系列調用,以通過刪除一組特殊字符來清理字符串:
let sanitizedString = string
.replacingOccurrences(of: "@", with: "")
.replacingOccurrences(of: "#", with: "")
.replacingOccurrences(of: "<", with: "")
.replacingOccurrences(of: ">", with: "")
上面的實現的問題是,它將導致我們的字符串進行4次單獨的迭代——使用較短的字符串,或者在不經常遇到的代碼路徑中進行上述操作時,這可能不是問題,但可能會變成當我們需要最大性能時的瓶頸。
值得慶幸的是,Swift通常不需要我們在性能代碼和優雅代碼之間進行選擇,我們要做的就是切換到一種更合適的API,在Set
中這個API僅通過我們的字符串一次即可刪除其中包含的每個字符。,像這樣:
let charactersToRemove: Set<Character> = ["@", "#", "<", ">"]
string.removeAll(where: charactersToRemove.contains)
因此,從性能的角度來看,使我們的代碼更“Swifty”,有時我們要做的就是探索標準庫在面對給定任務時必須提供的內容,尤其是在集合,機會方面相當高,因為有一個優雅,簡單的API,它還為我們提供了出色的性能特征。
文章來自 John Sundell的What makes code “Swifty”?中關于Fast的內容
附幾個簡單性能優化例子:
- 在這篇文章也是用到了文中這個方法iOS - DeviceToken 解析來解析Token
- swift
filter
會創建全新的數組,且會對所有元素進行操作,例如:
bigArray.filter { someCondition }.count > 0
寫成如下形式性能更好:
bigArray.contains { someCondition }
這種做法會比原來快得多,主要因為兩個方面:它不會去為了計數而創建一整個全新的數組, 并且一旦找到了第一個匹配的元素,它就將提前退出。一般來說,你只應該在需要所有結果時才去選擇使用 filter
。
- swift
String
的prefix
總是從頭開始,例如:
extension String {
var allPrefixes1: [Substring] {
return (0...self.count).map(self.prefix) }
}
let hello = "Hello"
hello.allPrefixes1 // ["", "H", "He", "Hel", "Hell", "Hello"]
這段代碼看上去簡單,但是它非常低效。首先,它會遍歷一次字符串,來計算其?度,這沒什 么大問題。但是,之后 n + 1 次對 prefix
的調用中,每一次都是一個 O(n) 操作,這是因為 prifix
總是要從頭開始工作,然后在字符串上經過所需要的字符個數。在一個線性復雜度的處 理中運行另一個線性復雜度的操作,意味著算法復雜度將會是 O(n2)。隨著字符串?度的增?, 這個算法所花費的時間將以平方的方式增加。
如果可能的話,一個高效的字符串算法應該只對字符串進行一次遍歷,而且它應該操作字符串 的索引,用索引來表示感興趣的子字符串。這里是相同算法的另一個版本:
extension String {
var allPrefixes2: [Substring] {
return [""] + self.indices.map { index in self[...index] } }
}
let hello = "Hello"
hello.allPrefixes2 // ["", "H", "He", "Hel", "Hell", "Hello"]
上面的代碼依然需要迭代一次字符串,以獲取索引的集合 indices。不過,一旦這個過程完成, map 中的下標操作就是 O(1) 復雜度的。這使得整個算法的復雜度得以保持在 O(n)。
例子來自《Swift進階》一書原作者【德】Chris Eidhof(克里斯·安道夫) 【德】Ole Begemann (奧勒·畢格曼) 【德】Airspeed Velocity (空速網站),中文版由王巍譯
是什么使代碼 “Swifty”? —— Safe 介紹了如何有選擇地使用類型系統的各個方面和功能,以使我們的代碼更易于理解和使用。
是什么使代碼 “Swifty”? —— Expressive 介紹了如何使用表達性命名和API設計傳達我們的代碼意圖