當前位置:
首頁 > 科技 > Swift 五年,ABI 終於穩定了!

Swift 五年,ABI 終於穩定了!

等啊等,盼兒盼,終於在 2019 年的 WWDC 大會之前,Swift 5 正式發布了,而更讓大家想要奔走相告的是這一版本的 ABI 穩定了!

曾於 2017 年正式擔任 Swift 語言開發項目組主管 Ted Kremenek 於近日在 Swift 官方博客上正式宣布,Swift 5 發布了!而這一次他也成功完成了彼時走馬上任時立下的 flag,為 Swift 帶來 ABI 的穩定。如今 Swift 可以更好地為當前和未來版本的蘋果操作系統 macOS、iOS、tvOS 和 watchOS 服務。

與此同時,Swift 5 還引入了構建塊的新功能,包括重新實現 String、在運行時對執行內存的獨佔訪問和新數據類型,以及對動態可調用類型的支持。接下來,我們將一一探討 Swift 5 中的變化。

ABI 的穩定意味著什麼?

當前有關最新版本的 Swift 5,最為熱議的話題之一毋庸置疑就是「ABI 穩定性」。在此,要論 ABI 的穩定對 Swift 意義為何如此重大?

其實,從開發者角度而言,早在 4 年前 Swift 2.0 發布之際,大家就希望 Swift 中的 ABI 能夠穩定,因為只有這樣 Swift 才能算是一門成熟的編程語言,否則用程序員的話來調侃,「自從學了 Swift 之後,每年都要學一門新語言」。

從技術角度來看,ABI 的穩定指的是二進位介面穩定,這意味著 Swift 應用程序不用再包含用於 Swift 標準庫和 Swift SDK 的動態鏈接庫,這些基礎庫將會被植入系統中。簡而言之,以後如同 Objective-C runtime 一樣,Swift runtime 和標準庫會隨著 iOS、macOS、tvOS、watchOS 的發布一起被提供。這樣帶來最為直接的影響就是可以為 App 瘦身。

不過當前仍有局限性的是,ABI 穩定雖然可以讓 runtime 支持多個 Swift 版本,但是前提是蘋果應用的開發者和框架、庫的作者必須使用相同版本的編譯器。如果想要刪除此限制,根據 Swift 官方博客描述,庫作者需要一個當前正在實現的功能,稱之為Module Stability。通過這一點也可以預料到,在接下來的 Swift 5.1 版本中,Module Stability 應會是一個重要的目標。

那麼 ABI 的穩定是否完全是一件好事?其實不然,正如知名iOS開發者王巍(江湖人稱「喵神」)日前在自己的博客上(https://onevcat.com/2019/02/swift-abi/)所言:

在 ABI 穩定之前,Swift runtime 是作為開發工具的一部分,被作為庫打包到 App 中的。這樣一來,在開發時,我們可以隨意使用新版本 Swift 的類型或特性,因為它們的版本是開發者自己決定的。不過,當 ABI 穩定後,Swift runtime 變為了用戶系統的一部分,它從開發工具,變為了運行的環境,不再由我們開發者唯一決定。

那麼除了實現了 ABI 的穩定性之外,,在 Swift 5 上還有哪些功能特性更新?

Swift 語言更新

新特性:

現在可以使用增強分隔符來表示字元串文字。在引號的前面加上一個或多個「#」,並以 # 號結尾,此時它會將反斜杠以及雙引號視為字元。使用增強分隔符可以避免將包含多個雙引號或反斜杠字元的字元串文本與額外的轉義符混淆。

如果聲明的類型與標準庫中的類型重名,那麼你所聲明的類型會影響標準庫中的類型。

@dynamicCallable 屬性允許你調用命名類型,就像使用簡單的語法糖調用函數一樣。主要用例是動態語言互操作性。

關鍵路徑現在支持 keypath (.self),一個引用其整個輸入值的 WritableKeyPath。

在 Swift 5 之前,你可以編寫一個帶有可變參數的枚舉案例:

但現在你要這麼做的話,可能會出現錯誤。這樣情況下,你可以將參數改成一個數組,並且顯示傳入數組:

在 Swift 5 模式下,可以用 ? 和 Optional 類型的表達式來扁平化生成的 Optional,而不是返回嵌套的 Optional。

如果類型 T 符合 Initialized with Literals 中的一個協議,例如ExpressibleByIntegerLiteral,並假設 literal 是一個文字表達式,那麼T(literal)使用相應的協議創建一個 T 類型的文字,而不是調用 T 的初始化器協議的默認文字類型的值。

字元串插值提高了性能、清晰度和效率。

Swift 標準庫

Swift 5 中的標準庫包括以下新功能:

標準庫現在包括帶有 Result.success(_ :) 和 Result.failure(_ :) 兩種情況的 Result 枚舉。在 do-catch 語句和 try 表達式無法使用的情況下,可以使用 Result手動傳遞和處理錯誤,譬如當你使用可能失敗的非同步 API 時。

作為此添加的一部分,錯誤協議現在遵循自身,這使得更容易處理通用上下文中的錯誤。

如今的標準庫定義了 SIMD 類型和基本運算符。simd 框架提供的類型,如 float2 和 float3,現在是新標準庫類型的類型別名。

SIMD 類型在標量元素類型上是通用的。例如,舊的 float3 類型是 SIMD3 的類型別名。遵循 SIMDScalar 協議的任何類型都可以用作 SIMD 向量的標量類型,但有效的向量化取決於選擇良好的數據布局並為相關的 SIMDS 存儲類型提供有效的下標操作。

大多數使用 simd 類型的現有代碼將可以繼續使用新的通用 SIMD 類型,但需要注意一些變化。

新類型增加了一些新的一致性,SIMD 向量現在是 Hashable、Equatable 和 Codable。這將允許開發者刪除一些現有代碼中提供的一致性的擴展。

通過重載可以提供向量-標量演算法的運算符集。這使得編寫一些代碼變得更容易,但在某些情況下會給類型檢查器帶來歧義,並且可能需要拆分某些表達式或使用顯式類型進行注釋。

由於類型現在是通用的泛型而不是具體的,如果開發者已經在 simd 框架類型上定義了自己的協議,則可能需要重構的一致性,因為 Swift 泛型類型不能對協議具有多個條件一致性。這種情況相對較少,但你通常需要做的是重構代碼,如下所示:

要改為使用以下結構:

這種更改通常允許用戶刪除許多冗餘實現,但它要求你定義任何必要的實現 hooks,這些 hooks 引用 Darwin 系統上標量類型的 C 頭文件中的具體函數。

Set 和 Dictionary 現在為每個新創建的實例使用不同的哈希種子。因此,相同Set 和 Dictionary中元素的順序與以前的版本相差較大:

如果錯誤地假定兩個不相關但相等的Set 和 Dictionary將包含相同順序的元素,那麼現有代碼在 Swift 5 中將更容易產生不正確的結果。雖然元素排序在不同的 Set 或 Dictionary 實例之間不穩定,但在同一個實例上進行多次迭代,順序不會變。除了強調這些集合不能保證一致的元素排序之外,這種變化還修復了許多大型操作(例如 union(_ :))表現出二次性能的情況。

為了幫助防止 Cocoa 對象出現不一致哈希,NSObject 上的 hashValue 屬性將不可以重寫。其實,早在 Swift 4.2 中已不推薦使用 hashValue 了。這樣情況下,可以在 NSObject 子類中重載 hash 屬性以自定義哈希值。示例實現如下:

在此,哈希與 equality 齊頭並進。如果重寫 hash,則還需要重寫 isEqual:,反之亦然。

DictionaryLiteral 類型被重命名為 KeyValuePairs。

橋接到 Objective-C 代碼的 Swift 字元串現在可以在適當的時候從 CFStringGetCStringPtr 返回一個非零值,並且從 UTF8String 方法返回的指針與字元串的生命周期相關聯。正確的程序應該不會出現任何問題,並且開發者還可以看到性能有著顯著的提升。但是,它也可能會導致以前未經測試的代碼在運行時暴露潛在的 Bug。

Sequence 協議不再具有 SubSequence 關聯類型。先前返回 SubSequence 的 Sequence 方法現在返回具體類型。例如,suffix(_:) 現在返回一個數組。

使用 SubSequence 的 Sequence 擴展應該修改為使用的具體類型,或者修改為 Collection 上的擴展,而此時 SubSequence 仍然可用。

String 結構的原生編碼從 UTF-16 切換到 UTF-8,與 String.UTF16View相比,這可以提高 String.UTF8View 的性能。

Swift 編譯器

Swift 5 默認為調試和發布版本強制執行對內存的獨佔訪問。Swift 5 支持動態可調用類型,有助於提高與 Python、JavaScript 和 Ruby 等動態語言的互操作性。

在編譯器方面,Swift 5 還實現了一些功能:

為了減小 Swift 元數據佔用的大小,Swift 中定義的便捷初始化器現在只提前分配一個對象,如果它們調用的是 Objective-C 中定義的指定初始方法。在大多數情況下,這對開發者的 App 沒有影響,但如果從 Objective-C 調用你的便捷初始化程序,並且不調用暴露給Objective-C的 self.init 方法,那麼從最初通過 alloc 分配的內存空間會在沒用調用任何初始化程序的情況下被釋放。對於不希望發生任何類型對象替換的初始化器的用戶來說,這可能會產生一些疑問。其中一個例子是使用 initWithCoder :: 方法,如果 NSKeyedUnarchiver 調用Swift 中的initWithCoder,並且歸檔對象圖包含循環,那麼 NSKeyedUnarchiver 的實現可能會出錯。

為了避免這種情況,請確保不支持對象替換的便捷初始化程序始終委託給也暴露給 Objective-C 的初始化程序,因為它們是在 Objective-C 中定義的,也是因為它們用 @objc 標記,或者因為它們覆蓋並暴露給 Objective-C 的初始化程序,還或者因為它們滿足 @objc 協議的要求。

Swift 中不再提供超過 16 位元組對齊的 C 類型。其實,Swift 編譯器也從未正確處理過這些類型。

在 Swift 5 模式中,非 final class 的便捷初始化程序中的 self 類型現在是動態 Self 類型,而不是具體類類型。

現在,在優化(-O 和 -Osize)構建中,默認情況下在運行時強制執行獨佔內存訪問。在運行時倘若程序違反排他性將會生成診斷消息:「Simultaneous accesses to […], but modification requires exclusive access」。對此,你可以使用命令行標誌 -enforce-exclusivity = unchecked 禁用此功能,但這樣做可能會導致未定義的行為。運行時違反排他性通常是由於同時訪問類屬性或全局變數(包括頂層代碼中的變數或轉義閉包捕獲的變數)。

Swift 3 模式已被刪除。 -swift-version 標誌支持的值為 4、4.2 和 5.0。

現在,默認參數列印在 SourceKit 生成的 Swift 模塊介面中,而不是僅使用佔位符默認值。

SE-0192:處理未來的 Enum 案件。在 Swift 5 模式中,需要通過 Objective-C 聲明或來自系統框架的枚舉來處理未知 case,其中包括可能在將來添加的 case,或者可能在 Objective-C 實現文件中私下定義的 case。形式上,Objective-C 允許在枚舉中存儲任何值,只要它適合底層類型即可。可以使用新的 @unknown 默認情況來處理這些未知 case,如果從 Swift 中省略任何已知情況,它仍會提供警告。它們也可以使用普通的默認情況進行處理。

如果你已在 Objective-C 中定義了自己的枚舉,並且不需要客戶端來處理未知案例,則可以使用 NS_CLOSED_ENUM 宏而不是 NS_ENUM。Swift 編譯器識別出這一點,並且不需要在迭代時提供默認值。

在 Swift 4 和 4.2 模式下,你仍然可以使用 @unknow 默認值。如果忽略它並將未知值傳遞給 switch,程序將在運行時報錯,這與 Xcode 10.1 中的 Swift 4.2 的行為相同。

unowned 和 unowned(unsafe)變數現在支持 Optional。

包管理器更新

Swift 5 為Swift 包管理器帶來了許多新功能:

SE-0219 :程序包管理器依賴鏡像。該依賴項鏡像允許頂層程序包覆蓋依賴關係 URL,可使用以下命令設置鏡像:

SE-0236:軟體包管理器平台部署設置。在使用 Swift 5 Package.swift 工具版本時,軟體包現在可以自定義 Apple 平台的最低部署目標設置。如果程序包的任何依賴項指定的最小部署目標大於程序包自身的最低部署目標,則構建程序包會拋出錯誤。

SE-0238:軟體包管理器目標特定的構建設置。如今在使用 Swift 5 Package.swift 工具版本時,目標可以聲明一些常用的特定於目標的構建設置。新設置可以基於平台和構建配置進行條件化。當前構建設置支持 Swift 和 C 語言,C 語言頭文件搜索路徑、鏈接庫和鏈接框架。

Swift 測試命令可以使用 --enable-code-coverage 標誌,以生成適合其他代碼覆蓋工具使用的代碼覆蓋率數據的標準格式。生成的代碼覆蓋率數據在 / / codecov 中可用。

Swift 5 不再支持 Swift 3 Package.swift 工具版本。Swift 3 Package.swift 工具版本上的軟體包應該更新到最新的工具版本。

目前對大包的包管理器操作現在明顯更快。

Swift 包管理器有一個新的 --disable-automatic-resolution 標誌,當 Package.resolved 條目不再與 Package.swift 清單文件中指定的依賴項版本兼容時,該標誌強制包解析失敗。此功能對於持續集成系統非常有用,可以檢查包的 Package.resolved 是否已過期。

Swift run 命令現在具有在 REPL 中導入庫而無需構建可執行文件的功能。即在 Swift run 命令新增了一個 --repl 選項,它將啟動 Swift REPL,支持導入包的庫目標。這使開發者可以輕鬆地從包目標中試用 API,而無需構建調用該 API 的可執行文件。

有關 Swift 包管理器的更多信息,可訪問:https://swift.org/getting-started/#using-the-package-manager。

遷移到 Swift 5

對於 Swift 開發者而言,這一次不用「每年再去學習一門新語言了」。最新版本的 Swift 5 與 Swift 4、Swift 4.1、Swift 4.2 兼容。

同時為了幫助開發者從早期版本的 Swift 遷移到 Swift 5 上,Apple 默認編譯器版本為 Swift 5.0 的 Xcode 10.2 中包含一個代碼遷移器,可以自動處理更改許多遷移所需的源代碼。或者你可以參考遷移指南:https://swift.org/migration-guide-swift5/

下載

Linux

基於 Ubuntu 18.04、Ubuntu 16.04 和 Ubuntu 14.04 的下載地址(https://swift.org/download/#releases);

Apple

在 Apple 平台上的開發,Swift 5 可以作為 Xcode 10.2 的一部分(https://itunes.apple.com/app/xcode/id497799835)或者也可以在Swift.org(https://swift.org/download/#release) 中下載。

參考:

https://developer.apple.com/documentation/xcode_release_notes/xcode_10_2_release_notes/swift_5_release_notes_for_xcode_10_2

https://onevcat.com/2019/02/swift-abi/

https://swift.org/blog/swift-5-released/

https://swift.org/blog/abi-stability-and-more/

熱 文推 薦

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 CSDN 的精彩文章:

如何做到毫秒級從百億大表任意維度篩選數據?| 技術頭條
人工智慧時代,我們需要什麼樣的晶元?

TAG:CSDN |