WWDC17 - What's New in Swift

官方視頻地址:https://developer.apple.com/videos/play/wwdc2017/402/

在這個近1個小時的視頻里,我們看到了 Swift 4 的新特性,Swift 團隊的方向以及開源社區的強大力量。

整個視頻分為五個小節,分別為:

  • 語言的優化與新功能(Language refinements and additions)
  • 源碼兼容性(Source compatibility)
  • 工具與性能(Tools and performance)
  • 標準庫(Standard library)
  • 內存的排他性存取(Exclusive access to memory)

下面我們就逐個解析吧。

一、語言的優化與新功能(Language refinements and additions)

  • extension中可以訪問到被private修飾的變量。這避免了有時將一些邏輯分離到extension中卻訪問不了相關的變量,然后不得不將private改成fileprivate的尷尬。??
  • 類與協議的組合。簡單來說就是下面的樣子:
var btns: [UIButton & MyProtocol]

&將類名和協議名組合起來,實現一些特定的能力。增加了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 中手動開啟,效果究竟如何有待商榷。
New Build System
  • 預編譯的橋接頭文件
    在 Xcode 9 中,對混編項目的橋接頭文件(bridging header)進行了預編譯,生成了一個 precompiled header, 加快了對這些橋接頭文件的解析速度。在大項目中這個優化尤為有效,因為我們不再需要在每個 Swift 文件編譯時再對橋接頭文件編譯,也就是說,減少了無謂的、重復的編譯。

    這項優化在 Xcode 9 中默認開啟。

預編譯的橋接頭文件
  • 覆蓋測試中使用共用構建(Shared Build for Coverage Testing)
    在 Xcode 8 中,執行測試構建會令工程構建兩次,其中一次是對整個工程的重新構建,以讓其包含冗余測試代碼 (extra instrumentation code)從而對代碼片段的執行次數進行計數;而另一次構建則是普通的構建,不包含冗余測試代碼。

    由于冗余測試代碼帶來的開銷非常小(less than 3%),因此在 Xcode 9 中,編譯器對此進行了優化,兩次構建變成了一次構建。

  • 構建時索引(Indexing While Building)
    在 Xcode 9 中,煩人的索引過程被挪到了構建時才執行。同樣開銷非常小。每次構建將更新索引,以實現更精準的索引結果。終于可以跟它說滾蛋了:
萬惡的Indexing
  • 可預測的性能(Predictable Performance)

    如果你看過去年 WWDC Session 416 Understanding Swift Performance 的話,應該知道什么是存在容器(Existential Container)

    簡單來說,就是協議類型在數組等集合類型中的數據結構。在存在容器中有一個緩存區,占 3 Words 的大小,在64位系統中就是 8 * 3 = 24 Bytes。如果存在容器裝得下某個小型的數據結構(比如有2個 Double 的 struct),那這個結構就會被存儲在緩存區里;否則會分配到堆中,然后將一個指向該堆地址的指針存儲在緩存區(棧)里。

    于是就有了下面這張圖:4 Words 的 struct 對比只有 1、2、3 Words 的 struct,會有一個性能開銷迅速爬升的情況(performance cliff)。這就是因為涉及到了堆內存的分配。

無法預見的性能瓶頸

視頻中還提到:“我們正在重新考量這個內聯緩存區的大小,但在 Swift 4 中,它依然是過去一樣占 3 個 Words 的大小”。

  • COW 存在容器

    ”那有 performance cliff 怎么辦呢?“

    ” 用??牛X版存在容器!“

    為了解決這個問題,Swift 團隊優化了存在容器,使其有了 COW(copy-on-write,寫時復制)能力。如此一來,分配在堆中的緩存區也有了引用計數,多個存在容器可以共用一個緩存區。當要寫內存時再根據 COW 的規則來走,減少了堆內存開銷。 因此在 Swift 4 中,存在容器將有一個更穩定可靠的性能表現。

    另外,對于泛型也有一項優化:將未具體化的泛型代碼(unspecialized generic code)所用的泛型緩存區(generic buffer)的內存分配位置,從原來的堆改成了 Swift 4 中的棧,以實現跟COW存在容器相似的優化效果。

COW優化后的存在容器
  • 更小的二進制包大小(Smaller Binaries)
    減小二進制包大小也就意味著用戶的 App 更小。Swift 4 中有這幾個方面的優化讓我們的 App 瘦身:

    1. 移除未使用的(協議)實現代碼。Swift 4 的編譯器可分析出哪些(協議)實現代碼是沒有被使用的,從而在打包時移除掉這些代碼。然而這個分析+移除的策略還不是那么完美,需要改動一下 Swift 的語法,也就是下面一項。

    2. 顯式寫 @objc 關鍵字以避免自動生成 Objective-C thunk 函數。原來啊,Swift 3 編譯時會幫 NSObject 的子類構建一份Objective-C 版本的 thunk 函數以供其 runtime 時調用。這些 thunk 函數雖 然最終還是調用 Swift 的實現,但也占二進制大小,還讓(1)中的優化無法實現。因此在 Swift 4 + Objective-C 的混編項目中,需要顯式寫 @objc 關鍵字,暴露需要用到的 Swift 方法。如果有多個方法要暴露給 Objective-C,建議你把它們放入 @objc extension 中。

Swift3會自動加入@objc關鍵字
遷移代碼的最后一步
  • 剝除 Swift 符號
    Swift 團隊為 libswiftCore 等核心庫減小了 symbol 的大小占用,方法是使用更簡潔的命名和剝除 Swift 符號。
    ?
    至于
    剝除符號(Symbol Stripping)
    的原因,請允許我翻譯一下視頻中的原話:

"靜態鏈接器和動態鏈接器分別用自己的字典樹來快速查找符號,也就是說 Swift 的 symbols 放在符號表中是基本上沒用的。"

因此在 Xcode 9 中,Strip Swift Symbols 默認開啟以優化我們工程中自己的 Swift 代碼;而對于系統庫的 Swift 代碼,則在 App Thinning 中才進行符號剝離,同樣有一個Strip Swift Symbols的選項可供勾選。

優化后的系統庫大小對比
工程中開啟 Symbol Stripping
導出時的 Symbol Stripping 選項

四、標準庫

這一小節主要講了Swift 字符串的優化、一些新語法和新的泛型特性。

  • Swift 4 處理復雜字素將更快(是 Swift 3 的3倍效率)。在 Swift 中,一個 Character 即一個字素(Grapheme)。所謂的“復雜字素”,從表面上看,是指如emoji、拉丁字母、漢字、日語假名等非英文亦非簡單字符的字素;從底層看,是那些無法通過下標隨機存取的字符集合。像這樣:

    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...,表示從strstartIndex到其endIndex

    再舉個粟子:

String 的 zipping
  • 截取部分字符串的 API 變了,返回的類型也變了。

    let str = "one,two,three"
    
    // Swift 3
    str.components(separatedBy: ",") // 返回 Array<String>
    
    // Swift 4
    str.split(separator: ",") // 返回新類型 Array<Substring>
    

    此舉有利有弊。

    利:不再執行深拷貝,減少內存分配和性能開銷;

    弊:原來的字符串大哥有可能被其切片后的小弟保持強引用,導致大哥釋放不了。

    因此,要在適當的時候顯式將Substring轉成String

    String與Substring共享緩存.png

  • 使用一對三雙引(""")來包裝多行字符串字面量。如下圖所示,注意縮進:(開源的力量!??)
三雙引
  • 新的泛型特性: protocolassociatedtype可以使用where子句,以實現對不同associatedtype之間的約束。視頻給出了 Swift 標準庫中利用這個新特性優化了 SequenceCollection的案例。

  • 新的泛型特性:泛型下標(Generic Subscripts)。也就是說可以這樣:

    extension Bar {
      subscript<T>(t: T) -> Foo { 
         //... 
      }
    }
    

    視頻給出了 Swift 標準庫中利用這個新特性實現了(上面第3點)的案例。

    ?

五、內存的排他性存取(Exclusive access to memory)

這是一個 Swift 4 的新特性,簡單而言就是,對有值語義(value types)集合類型變量的寫操作時,不能同時再觸發另一讀寫操作。或者也可以理解成,不能再觸發 copy-on-write 機制。

內存的排他性存取

也就是說,不讓下面這種事發生!在迭代時,閉包引用了numbers的緩存區,希望修改numbers內元素的值。如果此時執行numbers = [],那就會報錯:排他性存取!

非排他時觸發了COW

排他性存取的檢測分為編譯時運行時兩種。編譯時檢測可直接報錯;運行時檢測則會拋出異常,如下圖。

運行時報錯

有點像多線程的問題對吧?至此,我們只是在單線程下看這個排他性存取,而如果在多線程下觸發運行時的排他性存取,那就要通過 Thread Sanitizer 處理了。

當前,在 Swift 3.2 下的非排他性存取只會報 warning,但在未來的 Xcode 中會升級為 error。

內存的排他性存取保證了安全性的同時,也為從中優化了標準庫的設計。

在工程設置中可以調整 Exclusive Access to Memory 的策略。

WX20170613-211406@2x.png

總結

Swift 4 在 Swift 3 的基礎上升級,沒有像去年那樣巨大的遷移成本。升級后的 Swift 更快速、更安全,配合著 Xcode 9,生成的 App 體積將更小。

最后

此文粗略,或有錯漏,煩請指明,當天修正!

終于寫完了!全程無字幕聽著畫重點,哈哈!??
Let's Swift!??

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容