是什么使代碼 “Swifty”? —— Fast

Swift的官方網(wǎng)站上的About頁面列出了三個關(guān)鍵字:

  • 安全(Safe):為了最大限度地減少開發(fā)人員的錯誤;
  • 迅速(Fast):執(zhí)行的速度要快;
  • 表現(xiàn)力(Expressive):因為Swift的目標(biāo)是盡可能清晰易懂。

是什么使代碼 “Swifty”? —— Safe 介紹了如何有選擇地使用類型系統(tǒng)的各個方面和功能,以使我們的代碼更易于理解和使用。
是什么使代碼 “Swifty”? —— Expressive 介紹了如何使用表達(dá)性命名和API設(shè)計傳達(dá)我們的代碼意圖

Swifty Code —— Fast

性能之路(The path to performance)

Swift的第二個核心目標(biāo),就是要快一些,總的來說,這有點棘手。畢竟,編寫高性能代碼的主要部分在于測量,微調(diào)和再次測量。但是,使我們的代碼在性能方面與Swift本身更加一致的一種方法是,充分利用標(biāo)準(zhǔn)庫所提供的功能——特別是在處理集合(例如字符串)時。

就像我們在 Swift:字符串解析Swift:集合切片中看過一樣,Swift標(biāo)準(zhǔn)庫針對性能進(jìn)行了高度優(yōu)化,并且使我們能夠以高效的方式執(zhí)行許多常見的集合操作-假設(shè)我們使用正確的API。

例如,從字符串中刪除一組特定字符的一種常見方法是使用舊的ReplacementOccurences(of:with :)API,該API是Swift的String類型從其表親Objective-C的NSString繼承而來的。在這里,我們使用了對該API的一系列調(diào)用,以通過刪除一組特殊字符來清理字符串:

let sanitizedString = string
    .replacingOccurrences(of: "@", with: "")
    .replacingOccurrences(of: "#", with: "")
    .replacingOccurrences(of: "<", with: "")
    .replacingOccurrences(of: ">", with: "")

上面的實現(xiàn)的問題是,它將導(dǎo)致我們的字符串進(jìn)行4次單獨的迭代——使用較短的字符串,或者在不經(jīng)常遇到的代碼路徑中進(jìn)行上述操作時,這可能不是問題,但可能會變成當(dāng)我們需要最大性能時的瓶頸。

值得慶幸的是,Swift通常不需要我們在性能代碼和優(yōu)雅代碼之間進(jìn)行選擇,我們要做的就是切換到一種更合適的API,在Set中這個API僅通過我們的字符串一次即可刪除其中包含的每個字符。,像這樣:

let charactersToRemove: Set<Character> = ["@", "#", "<", ">"]
string.removeAll(where: charactersToRemove.contains)

因此,從性能的角度來看,使我們的代碼更“Swifty”,有時我們要做的就是探索標(biāo)準(zhǔn)庫在面對給定任務(wù)時必須提供的內(nèi)容,尤其是在集合,機會方面相當(dāng)高,因為有一個優(yōu)雅,簡單的API,它還為我們提供了出色的性能特征。

文章來自 John SundellWhat makes code “Swifty”?中關(guān)于Fast的內(nèi)容

附幾個簡單性能優(yōu)化例子:
  • 在這篇文章也是用到了文中這個方法iOS - DeviceToken 解析來解析Token
  • swift filter會創(chuàng)建全新的數(shù)組,且會對所有元素進(jìn)行操作,例如:
bigArray.filter { someCondition }.count > 0

寫成如下形式性能更好:

bigArray.contains { someCondition }

這種做法會比原來快得多,主要因為兩個方面:它不會去為了計數(shù)而創(chuàng)建一整個全新的數(shù)組, 并且一旦找到了第一個匹配的元素,它就將提前退出。一般來說,你只應(yīng)該在需要所有結(jié)果時才去選擇使用 filter

  • swift Stringprefix總是從頭開始,例如:
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 的調(diào)用中,每一次都是一個 O(n) 操作,這是因為 prifix總是要從頭開始工作,然后在字符串上經(jīng)過所需要的字符個數(shù)。在一個線性復(fù)雜度的處 理中運行另一個線性復(fù)雜度的操作,意味著算法復(fù)雜度將會是 O(n2)。隨著字符串?度的增?, 這個算法所花費的時間將以平方的方式增加。

如果可能的話,一個高效的字符串算法應(yīng)該只對字符串進(jìn)行一次遍歷,而且它應(yīng)該操作字符串 的索引,用索引來表示感興趣的子字符串。這里是相同算法的另一個版本:

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 中的下標(biāo)操作就是 O(1) 復(fù)雜度的。這使得整個算法的復(fù)雜度得以保持在 O(n)。

例子來自《Swift進(jìn)階》一書原作者【德】Chris Eidhof(克里斯·安道夫) 【德】Ole Begemann (奧勒·畢格曼) 【德】Airspeed Velocity (空速網(wǎng)站),中文版由王巍譯

是什么使代碼 “Swifty”? —— Safe 介紹了如何有選擇地使用類型系統(tǒng)的各個方面和功能,以使我們的代碼更易于理解和使用。
是什么使代碼 “Swifty”? —— Expressive 介紹了如何使用表達(dá)性命名和API設(shè)計傳達(dá)我們的代碼意圖

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容