官方視頻地址:https://developer.apple.com/videos/play/wwdc2017/402/
在這個近1個小時的視頻里喇肋,我們看到了 Swift 4 的新特性坟乾,Swift 團隊的方向以及開源社區(qū)的強大力量。
整個視頻分為五個小節(jié)蝶防,分別為:
- 語言的優(yōu)化與新功能(Language refinements and additions)
- 源碼兼容性(Source compatibility)
- 工具與性能(Tools and performance)
- 標(biāo)準(zhǔn)庫(Standard library)
- 內(nèi)存的排他性存壬趼隆(Exclusive access to memory)
下面我們就逐個解析吧。
一慧脱、語言的優(yōu)化與新功能(Language refinements and additions)
-
extension
中可以訪問到被private
修飾的變量渺绒。這避免了有時將一些邏輯分離到extension
中卻訪問不了相關(guān)的變量,然后不得不將private
改成fileprivate
的尷尬。?? - 類與協(xié)議的組合宗兼。簡單來說就是下面的樣子:
var btns: [UIButton & MyProtocol]
用&
將類名和協(xié)議名組合起來躏鱼,實現(xiàn)一些特定的能力。增加了POP的可用性殷绍,喜大普奔染苛!同時,一些 OC 的 API 終于有了完美的 Swift 寫法主到,如視頻中的提及的:
二茶行、源碼兼容性
Swift 4 的與 Swift 3 的差別不大,至少不像從 Swift 2 到 3 這么大登钥。這下大家可以安心升級到 Swift 4 了吧畔师?
在 Xcode 9 中,Swift 會以 4 和 3.2 兩個版本兼容存在牧牢。我們可以對不同的 target 選擇不同的版本進行編譯看锉,比如:App 用 Swift 4 編譯,而某些第三方庫沒有更新塔鳍,依然可以用 Swift 3.2 來編譯伯铣。
三、工具與性能
-
新的 Build System
更低的性能開銷(特別是在大項目上)轮纫。使用新的 Build System 目前需要在 Project Settings / Workspace Settings 中手動開啟腔寡,效果究竟如何有待商榷。
-
預(yù)編譯的橋接頭文件
在 Xcode 9 中掌唾,對混編項目的橋接頭文件(bridging header)進行了預(yù)編譯放前,生成了一個 precompiled header, 加快了對這些橋接頭文件的解析速度郑兴。在大項目中這個優(yōu)化尤為有效犀斋,因為我們不再需要在每個 Swift 文件編譯時再對橋接頭文件編譯,也就是說情连,減少了無謂的叽粹、重復(fù)的編譯。這項優(yōu)化在 Xcode 9 中默認(rèn)開啟却舀。
-
覆蓋測試中使用共用構(gòu)建(Shared Build for Coverage Testing)
在 Xcode 8 中虫几,執(zhí)行測試構(gòu)建會令工程構(gòu)建兩次,其中一次是對整個工程的重新構(gòu)建挽拔,以讓其包含冗余測試代碼 (extra instrumentation code)從而對代碼片段的執(zhí)行次數(shù)進行計數(shù)辆脸;而另一次構(gòu)建則是普通的構(gòu)建,不包含冗余測試代碼螃诅。由于冗余測試代碼帶來的開銷非常蟹惹狻(less than 3%)状囱,因此在 Xcode 9 中,編譯器對此進行了優(yōu)化倘是,兩次構(gòu)建變成了一次構(gòu)建亭枷。
-
構(gòu)建時索引(Indexing While Building)
在 Xcode 9 中,煩人的索引過程被挪到了構(gòu)建時才執(zhí)行搀崭。同樣開銷非常小叨粘。每次構(gòu)建將更新索引,以實現(xiàn)更精準(zhǔn)的索引結(jié)果瘤睹。終于可以跟它說滾蛋了:
-
可預(yù)測的性能(Predictable Performance)
如果你看過去年 WWDC Session 416 Understanding Swift Performance 的話升敲,應(yīng)該知道什么是存在容器(Existential Container)。
簡單來說轰传,就是協(xié)議類型在數(shù)組等集合類型中的數(shù)據(jù)結(jié)構(gòu)驴党。在存在容器中有一個緩存區(qū),占 3 Words 的大小绸吸,在64位系統(tǒng)中就是 8 * 3 = 24 Bytes鼻弧。如果存在容器裝得下某個小型的數(shù)據(jù)結(jié)構(gòu)(比如有2個 Double 的 struct)设江,那這個結(jié)構(gòu)就會被存儲在緩存區(qū)里锦茁;否則會分配到堆中,然后將一個指向該堆地址的指針存儲在緩存區(qū)(棧)里叉存。
于是就有了下面這張圖:4 Words 的 struct 對比只有 1码俩、2、3 Words 的 struct歼捏,會有一個性能開銷迅速爬升的情況(performance cliff)稿存。這就是因為涉及到了堆內(nèi)存的分配。
視頻中還提到:“我們正在重新考量這個內(nèi)聯(lián)緩存區(qū)的大小瞳秽,但在 Swift 4 中瓣履,它依然是過去一樣占 3 個 Words 的大小”。
-
COW 存在容器
”那有 performance cliff 怎么辦呢练俐?“
” 用??牛X版存在容器袖迎!“
為了解決這個問題,Swift 團隊優(yōu)化了存在容器腺晾,使其有了 COW(copy-on-write燕锥,寫時復(fù)制)能力。如此一來悯蝉,分配在堆中的緩存區(qū)也有了引用計數(shù)归形,多個存在容器可以共用一個緩存區(qū)。當(dāng)要寫內(nèi)存時再根據(jù) COW 的規(guī)則來走鼻由,減少了堆內(nèi)存開銷暇榴。 因此在 Swift 4 中厚棵,存在容器將有一個更穩(wěn)定可靠的性能表現(xiàn)。
另外蔼紧,對于泛型也有一項優(yōu)化:將未具體化的泛型代碼(unspecialized generic code)所用的泛型緩存區(qū)(generic buffer)的內(nèi)存分配位置窟感,從原來的堆改成了 Swift 4 中的棧,以實現(xiàn)跟COW存在容器相似的優(yōu)化效果歉井。
-
更小的二進制包大惺疗怼(Smaller Binaries)
減小二進制包大小也就意味著用戶的 App 更小。Swift 4 中有這幾個方面的優(yōu)化讓我們的 App 瘦身:移除未使用的(協(xié)議)實現(xiàn)代碼哩至。Swift 4 的編譯器可分析出哪些(協(xié)議)實現(xiàn)代碼是沒有被使用的躏嚎,從而在打包時移除掉這些代碼。然而這個分析+移除的策略還不是那么完美菩貌,需要改動一下 Swift 的語法卢佣,也就是下面一項。
顯式寫 @objc 關(guān)鍵字以避免自動生成 Objective-C thunk 函數(shù)箭阶。原來啊虚茶,Swift 3 編譯時會幫 NSObject 的子類構(gòu)建一份Objective-C 版本的 thunk 函數(shù)以供其 runtime 時調(diào)用。這些 thunk 函數(shù)雖 然最終還是調(diào)用 Swift 的實現(xiàn)仇参,但也占二進制大小嘹叫,還讓(1)中的優(yōu)化無法實現(xiàn)。因此在 Swift 4 + Objective-C 的混編項目中诈乒,需要顯式寫 @objc 關(guān)鍵字罩扇,暴露需要用到的 Swift 方法。如果有多個方法要暴露給 Objective-C怕磨,建議你把它們放入 @objc extension 中喂饥。
-
剝除 Swift 符號 。
Swift 團隊為 libswiftCore 等核心庫減小了 symbol 的大小占用肠鲫,方法是使用更簡潔的命名和剝除 Swift 符號员帮。
?
至于剝除符號(Symbol Stripping)的原因,請允許我翻譯一下視頻中的原話:
"靜態(tài)鏈接器和動態(tài)鏈接器分別用自己的字典樹來快速查找符號导饲,也就是說 Swift 的 symbols 放在符號表中是基本上沒用的捞高。"
因此在 Xcode 9 中,Strip Swift Symbols
默認(rèn)開啟以優(yōu)化我們工程中自己的 Swift 代碼帜消;而對于系統(tǒng)庫的 Swift 代碼棠枉,則在 App Thinning 中才進行符號剝離,同樣有一個Strip Swift Symbols
的選項可供勾選泡挺。
四辈讶、標(biāo)準(zhǔn)庫
這一小節(jié)主要講了Swift 字符串的優(yōu)化、一些新語法和新的泛型特性娄猫。
-
Swift 4 處理復(fù)雜字素將更快(是 Swift 3 的3倍效率)贱除。在 Swift 中生闲,一個
Character
即一個字素(Grapheme)。所謂的“復(fù)雜字素”月幌,從表面上看碍讯,是指如emoji、拉丁字母扯躺、漢字捉兴、日語假名等非英文亦非簡單字符的字素;從底層看录语,是那些無法通過下標(biāo)隨機存取的字符集合倍啥。像這樣:var famaily = "??" famaily += "\u{200D}??" famaily += "\u{200D}??" famaily += "\u{200D}??" print(famaily) // ??????????? print(famaily.characters.count) // Swift3輸出“4”,Swift4輸出“1“
Swift 4 的字符串本身就是一個字符集合澎埠。也就是說虽缕,不需要再寫
str.characters.count
了,直接str.count
蒲稳。-
一個新語法:
str.startIndex...
氮趋,表示從str
的startIndex
到其endIndex
。再舉個粟子:
-
截取部分字符串的 API 變了江耀,返回的類型也變了剩胁。
let str = "one,two,three" // Swift 3 str.components(separatedBy: ",") // 返回 Array<String> // Swift 4 str.split(separator: ",") // 返回新類型 Array<Substring>
此舉有利有弊。
利:不再執(zhí)行深拷貝决记,減少內(nèi)存分配和性能開銷摧冀;
弊:原來的字符串大哥有可能被其切片后的小弟保持強引用,導(dǎo)致大哥釋放不了系宫。
因此,要在適當(dāng)?shù)臅r候顯式將
Substring
轉(zhuǎn)成String
建车!
- 使用一對三雙引(""")來包裝多行字符串字面量扩借。如下圖所示,注意縮進:(開源的力量缤至!??)
新的泛型特性:
protocol
的associatedtype
可以使用where
子句潮罪,以實現(xiàn)對不同associatedtype
之間的約束。視頻給出了 Swift 標(biāo)準(zhǔn)庫中利用這個新特性優(yōu)化了Sequence
和Collection
的案例领斥。-
新的泛型特性:泛型下標(biāo)(Generic Subscripts)嫉到。也就是說可以這樣:
extension Bar { subscript<T>(t: T) -> Foo { //... } }
視頻給出了 Swift 標(biāo)準(zhǔn)庫中利用這個新特性實現(xiàn)了
…
(上面第3點)的案例。?
五月洛、內(nèi)存的排他性存群味瘛(Exclusive access to memory)
這是一個 Swift 4 的新特性,簡單而言就是嚼黔,對有值語義(value types)的集合類型變量的寫操作時细层,不能同時再觸發(fā)另一讀寫操作惜辑。或者也可以理解成疫赎,不能再觸發(fā) copy-on-write 機制盛撑。
也就是說,不讓下面這種事發(fā)生捧搞!在迭代時抵卫,閉包引用了numbers
的緩存區(qū),希望修改numbers
內(nèi)元素的值胎撇。如果此時執(zhí)行numbers = []
陌僵,那就會報錯:排他性存取创坞!
排他性存取的檢測分為編譯時和運行時兩種碗短。編譯時檢測可直接報錯;運行時檢測則會拋出異常题涨,如下圖偎谁。
有點像多線程的問題對吧?至此纲堵,我們只是在單線程下看這個排他性存取巡雨,而如果在多線程下觸發(fā)運行時的排他性存取,那就要通過 Thread Sanitizer 處理了席函。
當(dāng)前铐望,在 Swift 3.2 下的非排他性存取只會報 warning,但在未來的 Xcode 中會升級為 error茂附。
內(nèi)存的排他性存取保證了安全性的同時正蛙,也為從中優(yōu)化了標(biāo)準(zhǔn)庫的設(shè)計。
在工程設(shè)置中可以調(diào)整 Exclusive Access to Memory 的策略营曼。
總結(jié)
Swift 4 在 Swift 3 的基礎(chǔ)上升級乒验,沒有像去年那樣巨大的遷移成本。升級后的 Swift 更快速蒂阱、更安全锻全,配合著 Xcode 9,生成的 App 體積將更小录煤。
最后
此文粗略鳄厌,或有錯漏,煩請指明妈踊,當(dāng)天修正了嚎!
終于寫完了!全程無字幕聽著畫重點响委,哈哈新思!??
Let's Swift!??