百度App Objective-C/Swift 组件化混编之路(一)
作者丨郭金、陳佳
來源丨百度App技術
一. 背景
1.1 Swift 發展歷史
-
2010 年 7 月,克里斯(Chris Lattner)開始設計 Swift。完成基礎架構后,克里斯帶領開發小組陸續完成語法設計、編譯器、運行時、框架、IDE 和文檔等相關工作。
-
WWDC 2014,經歷四年的開發,Swift 發布。
-
WWDC 2015,Swift 2.0,蘋果宣布 Swift 開源,包含編譯器和標準庫。這一階段發展迅速,變動也非常頻繁。因此開發者也都處于嘗試或觀望狀態。
-
2016 Swift 3.0,是語法和接口變化最大的一個版本,大量直接從 OC 移植過來的方法名被簡化了,大量常用 Foundation 的類在 Swift 中改成了去掉 "NS" 前綴的結構體和類,原來 OC 方法名的很多描述性語句變成了 label,一些開發框架中的 C 語法 API(如 GCD、CoreGraphics等)也統一了風格, Swift 語法風格基本定型。
-
2019 Swift 5.0,ABI 穩定,并且向后兼容。2019 年 3 月,Swift 5.0 正式發布。目前,Swift 的當前版本包含跨 Apple 平臺的應用程序二進制接口(ABI)的穩定版本。這是朝著幫助開發人員在專用操作系統(如 iOS,macOS,tvOS,watchOS 和 iPadOS)上使用Swift邁出的一大步。蘋果正在構建一個堅實的生態系統,因為現在標準的 Swift 庫已包含在 OS 版本中。
1.2 ABI 穩定
ABI(Application Binary Interface),即應用程序二進制接口,描述了應用程序和操作系統之間,一個應用和它的庫之間的接口。在 iOS 和 macOS 平臺,Swift 編寫的二進制程序在運行時通過 ABI 與其他程序庫或組件進行交互。程序的編譯會產生一個或者多個二進制實體,這些二進制實體必須在一些很底層的細節上達成一致,才能被鏈接在一起執行。可以說ABI就是一個規范,一種協議。它會規定如何調用函數,如何在內存中表示數據,甚至是如何存儲和訪問 metadata。Xcode 10.2 集成了 Swift 5.0 編譯器,只要使用這個版本以上的編譯器,編譯出來的二進制就是 ABI 穩定的。
在此示例中,使用 Swift 5.0 構建的應用程序將在安裝了 Swift 5 標準庫的系統以及 Swift 5.1 或將來的 Swift 6 的系統上運行。
ABI 穩定的好處:
-
減小包體積:iOS 12.2 以前版本,用 Swift 開發的 App 打包時需要將當前版本的 Swift 內置動態庫打包進去;而 iOS 12.2 及以上版本,系統內置了 Swift 動態庫,不用每個 App 單獨內置,大大減小了包體積(實際減小的包體積大小和 App 用到的 Swift 標準庫和系統版本都有關系)。
-
節省內存:所有 App 使用同一個 Swift 運行時, App 啟動不需要額外加載內置 Swift 動態庫,在 iOS 12.2 及以上系統會更節省內存。
-
減小啟動耗時:同理,在 iOS 12.2 及以上系統因為啟動時無需加載內置 Swift 動態庫,也節省了啟動耗時。
-
不強依賴編譯器:ABI 穩定之前,兩個二進制組件需在同一編譯器下編譯。ABI 兼容性保障應用程序和各二進制組件可以分開編譯,不強依賴編譯器,應用和二進制形態組件也不用在同一編譯器下編譯。
-
Pure Swift:對于 Apple 工程師來說,可以直接在系統框架使用 Swift,而不必用 Objective-C 來 wrap 一遍,運行效率更高,維護成本更低。得益于 ABI 穩定,在 2019 年,Apple 也確實推出了 SwiftUI、RealityKit、Combine、CreateML 4個 Pure Swift 框架。
1.3 Module 穩定
Swift 庫和庫的 API 以 module 的方式導出,module 文件被編譯創建和使用。Swift 5.1 引入了穩定基于文本的 module 接口文件,不同版本的編譯器具備兼容性。Module 穩定使我們創建的 Swift framework 能夠兼容未來的 Swift 版本。Module 穩定代表著描述模塊 API 的信息格式穩定。這個信息會在編譯時使用,它表明了這個庫所有的類和函數都是什么,如同 C 語言的 header 文件一樣。Swift 把這個信息存在一個名為 .swiftmodule 的二進制文件中。由于這個文件在不同編譯器間不兼容,這意味著如果應用程序開發人員無法使用其他版本的 Swift 編譯器引入該 framework。
Swift 5.1 實現了一個文本的方案來實現 Module 穩定,使用一個名為 .swiftinterface 的文本文件替換二進制的 .swiftmodule 文件,內容類似于 Xcode 中 swift 文件的 generated interface。
例如,你可以使用 Swift 6 構建框架,而該框架的接口將可由 Swift 6 和以后的 Swift 7 編譯器讀取。
Swift 不同版本的編譯器編譯出的產物可以互相引用,不會出現以下錯誤:
“Module compiled with Swift 5.1 cannot be imported by the Swift 5.2 compiler”?
1.4 Library Evolution
Library Evolution 允許二進制組件發生變化時,不用再重新編譯其宿主。這樣無論是系統框架發生變化時,還是開發者依賴的第三方框架發生變化時,開發者都不需要重新編譯自己的應用(或框架)就能直接運行。
在此示例中,應用是基于框架的原始版本(黃色)構建的。由于 Library Evolution,它可以運行在包含黃色版本框架的系統上,也可以直接運行在升級后的紅色版本框架的系統上。
1.5 ABI 穩定 、 Module 穩定和 Library Evolution
| ABI 穩定 | 應用程序能在更高版本的 Swift 標準庫環境下運行 | Swift 5 之后開始支持 |
| Module 穩定(并且 ABI 穩定) | 組件能在更高版本的編譯器環境下被引用 | Swift 5.1 之后開始支持 |
| Library Evolution | 二進制組件在發生改變時,只要 API 向前兼容,引用它的宿主無須重新編譯 | Swift 5.1 之后開始支持 |
1.6 開源社區:
從圖中可以看到,從 2016 年年中開始,github Swift 代碼 push 的量已經超過了 OC,Swift 的新活躍開源項目也遠超過 OC。開源推動了外部貢獻者參與 Swift 生態建設,長期看 OC 三方開源庫面臨年久失修和新功能不支持的風險。
1.7 官方推薦
Apple 已經在開發文檔中將 Swift 設置為默認示例語言,2019年 WWDC 發布了4個純 Swift 的 Framework,不排除后面發布的 Framework 只有 Swift 版本的可能;另外 Apple 也積極開展高校合作,目前在美國已經有 18 所大學和學院將 Swift 納入教學課程,同時也開發了面向高中和高等院校的《使用 Swift 開發》課程,和面向 4 年級至 8 年級學生的《人人能編程》課程;隨著 Swift 語言的普及和編程門檻的降低,我們能做的就是為優秀的 Swift 開發者敞開大門。
另外,OC 和 Swift 也有良好的互操作性。至此,所有準備工作已就緒,對 iOS 開發者來說普及已經是大勢所趨了。
二. Swift 的優勢
2.1 開發效率
Swift 擁有簡單而富有表現力的語法,即使你以前沒有任何編碼經驗,也很容易理解。事實上,根據蘋果公司的說法,Swift 被設計成第一種供任何人學習的編程語言。Swift 也同時吸收了很多語言的特性, 以至于各種不同語言的開發者寫 Swift 的時候都會覺得特別熟悉特別親切。以下是 Swift 從其他語言借鑒的特性或表示方法:
-
Dictionaries(或Hash Table):簡潔的中括號初始化語法從 JavaScript 而來。
-
數據類型推斷:編譯器通過變量的初始值很容易推斷變量的數據類型。這個功能最早出現在 Haskell、Scala、Opa、微軟也在 .Net 3.0 中引入了這個功能。
-
泛型數據結構聲明:Java 5 引入了泛型,通過尖括號中的數據類型,告訴編譯器 HashMap,Array,Collection 等集合類中存儲了何種數據類型。同一時間微軟把這一功能引入到 C# 中。
-
字符串模板:從 Cold Fusion、JSP 等語言而來。
-
程序行尾可選分號:從 JavaScript、Python 而來。
-
Protocol(或 Interface):從 Java 和 C# 而來。
-
元組(Tuples):元組是通過逗號分割,小括號括起來的類型列表,可以讓一個函數有多個返回值,這種特性從 Lisp 和 Python 而來。?
-
閉包:閉包的概念出現于 60 年代,最早實現閉包的程序語言是 Scheme。之后,閉包被廣泛使用于函數式編程語言如 ML 語言和 LISP。
-
協程(async/await):即將引入的協程,作為取代線程的更優雅的異步模型,來源于 Smalltalk、Ruby、Lua、Julia 和 Go 等語言。(Coming soon)
畢加索:(Good artists borrow, Great artists steal)
2.2 安全
Swift 語言的語法鼓勵你編寫簡潔一致的代碼,有時甚至會變得嚴格,同時提供了保護措施以防止錯誤并提高可讀性。-
靜態類型語言:IDE 在編譯前就會檢查代碼中的錯誤,類型檢查更加嚴格。
-
類型推斷:編譯器可以在編譯代碼的時候通過表達式的值自動推斷出表達式的類型。
-
guard: 我們可以使用 guard 語句來對后面的代碼塊加以保護,使代碼塊在未滿足條件時能提前正常返回(return)或結束循環(break/continue)或異常退出(throw/fatalError())。
-
optional:默認情況下,Swift 對象不能定為 nil — 這在另一方面保證了 Swift 的安全性。實際上,Swift 編譯器會在你嘗試創建或使用 nil 對象時顯示編譯時錯誤,阻止你繼續操作。這使得代碼編寫變得更簡潔、更安全,并且可以防止 app 中出現大量的運行時崩潰。但是,在某些情況下,運用 nil 是適當合理的。針對這類情況,Swift 提供了一項創新功能,稱為“可選類型”。可選類型可以包含 nil,但是 Swift 語法會強制要求你使用 ? 語法來安全地處理 nil。使用該語法,等于向編譯器表明你理解此行為并將安全地進行處理。
2.2 編譯優化
-
SIL(Swift Intermediate Language):SIL 會對 Swift 進行較高級別的語義分析和優化。包括高級別的語義分析,診斷轉換,去虛擬化,特化,引用計數優化,TBAA(Type Based Alias Analysis)等。?
-
WMO:全模塊編譯優化。首先,編譯器了解模塊中所有函數的實現,所以它能夠執行諸如函數內聯和函數特殊化等優化。函數特化主要應該是指通過調用上下文傳遞的類型來生成一個基于特定類型的版本。但是很多情況下,會導致二級制級別的代碼膨脹。有了全模塊優化,能夠把多處基于相同類型生成的函數優化為一個。
2.3 運行性能
-
靜態類型語言:靜態類型語言比動態類型語言更快,因為類、方法、數據類型定義更清晰,編譯器可以進行內聯等優化、減少為支持引用類型而額外分配的內存空間,同時由于采用靜態派發的方式也大幅提高了運行時方法的調用效率。
-
靜態派發:是在編譯期就能確定的調用方法的派發方式。靜態派發相比于動態派發更快,而且靜態派發還會進行內聯等一些優化,減少函數的尋址及內存地址的偏移計算等一系列操作,使函數的執行速度更快,性能更高。
-
Fast, Whole Module Optimization:開啟 -O -whole-module-optimization,Swift 編譯器將會同時考慮整個 module 中所有源碼的情況,并將那些沒有被繼承和重載的類型和方法標記為 final,這將盡可能地避免動態派發的調用,或者甚至將方法進行內聯處理以加速運行。
-
結構體:結構體除了屬性的存儲更安全、效率更高之外,其函數的派發也更高效。由于結構體不能被繼承,也就是結構體的類型被 final 修飾,其內部函數應該是屬于靜態派發,在編譯期就確定了函數的執行方式,其函數的調用通過內聯(inline)的方式進行優化,其內存連續,減少了函數的尋址及內存地址的偏移計算,其運行相比于動態派發更加高效。
-
Swift 的運行效率甚至能比肩 C++。可以參考 http://www.primatelabs.com/blog/2014/12/swift-performance/?對 Swift 和 C++ 性能的比較。
2.4 內存管理
-
ARC:OC ARC 支持范圍包含 Cocoa Touch framework,但不包含 CoreGraphics、CoreFoundation 等底層框架;Swift 所有 API 都支持 ARC。
-
COW:Copy On Write,寫時復制,Swift 針對標準庫中的集合類型(Array、Dictionary、Set)進行優化,當變量指向的內存空間并沒有發生改變,進行拷貝時,并沒有真正發生拷貝,而是指向原來的內存。只有當值發生改變時才會進行深拷貝。
-
值類型:在 Swift 中定長的值類型都是保存在棧上的,操作時不會涉及堆上的內存。變長的值類型(字符串、集合類型是可變長度的值類型)會分配堆內存。
-
引用類型:引用類型的存儲屬性不會直接保存在棧上,系統會在棧上開辟空間用來保存實例的指針,棧上的指針負責去堆上找到相應的對象。
-
所有權:獨占性原則——阻止以互相沖突的方式同時訪問某個變量 ;允許被“共享”的值傳遞下去;允許標記某個類型不能被隱式復制。(Coming soon)
三. Swift 使用現狀
3.1 百度App 及矩陣產品
全量工程化和工程改造前,百度App 在 Watch App、各獨立 Widget、以及很少一部分 SDK 中使用 Swift。不過百度內部 檸檬愛美、古物潮玩、有噗等創新產品都廣泛的使用 Swift 開發。
3.2 國內其他 App
根據現有公開資料,我們了解到手淘已全面支持 Swift,微信部分使用 Swift,今日頭條的飛書(Lark)使用了 Swift。
3.3 國外 App
這篇文章(https://blog.csdn.net/Desgard_Duan/article/details/105872728)統計了國內外使用的 Swift 開發的 Top 100 應用列表,但并不明確各 App Swift 的應用范圍,也不明確各 App 已經系統化解決 OC 和 Swift 在大型工程或組件化開發模式下的問題。
四. 影響面評估
4.1 應用體積的影響
對于 iOS 12.2 以下的系統,使用 Swift 編寫的代碼打包成 App 后需要額外內置 Swift 動態庫。而 iOS 12.2 及以上版本,使用 Swift 5.0 以上編寫的 Swift 代碼,不再需要打包 Swift 運行時,相比之前,減小了 7.9 MB。
在"Swift 的優勢"部分,我們已經看到 Swift 在開發效率、安全性、運行性能、內存管理方面的優勢,也無明顯劣勢,所以普及也是順其自然的事情了。
五. 落地步驟
在單工程源碼模式下,我們為 Swift 訪問 OC 建立 bridge 文件,Swift 編譯時也會生成 *-Swift.h 頭文件供 OC 調用;這樣就可以實現 OC 和 Swift 混合開發。
在以并行開發,或者以 App 工廠為目標的 App ,都會對工程進行組件化拆分,不僅需要依賴管理工具的支撐,也需要對工程進行改造。百度App 目前使用組件化模式開發,同時兼容 源碼、二進制 兩種組件形態;因此需要保障組件間互操作性,保障對源碼、二進制兩種形態下的組件編譯、鏈接過程的完整支撐。
百度App 整體落地步驟如下:
-
Swift 優勢調研
-
影響面評估
-
編碼規范制定及約束工具開發
-
EasyBox 工具鏈混編支持
-
工程改造與實踐
這篇文章介紹了前兩部分,Swift 編碼規范及約束工具有創新團隊同學提供支持。后面兩篇文章將重點介紹這里面的兩個重點環節,工具鏈層面系統化支持 OC 和? ?Swift 混編,以及如何進行工程改造以支撐全面Swift全面應用開發。
六. 參考
-
WWDC 2019 - What's New In Swift:
https://devstreaming-cdn.apple.com/videos/wwdc/2019/402fd460n3p3w5c/402/402_whats_new_in_swift.pdf?dl=1
-
WWDC 2016 - Understanding Swift Performance:
https://developer.apple.com/videos/play/wwdc2016/416/
-
What is Module Stability in Swift and why should you care?
https://www.donnywals.com/what-is-module-stability-in-swift-and-why-should-you-care/
-
ABI Stability and More:
https://swift.org/blog/abi-stability-and-more/
-
一次關于 Swift 在 iOS 生態圈里的現狀調研:
https://blog.csdn.net/Desgard_Duan/article/details/105872728
- 手淘航母級 App 戀上 Swift 之路: https://mp.weixin.qq.com/s/_ecHx0-r_od1-AfpYNvchg
-
Swift vs Objective-C: Out with the Old, In with the New:
https://www.altexsoft.com/blog/engineering/swift-vs-objective-c-out-with-the-old-in-with-the-new/ -
10 features Apple 'stole' for the Swift programming language:
https://www.infoworld.com/article/2606431/155797-10-prominent-features-stolen-by-Apple-s-Swift-and-where-they-came-fro.html#slide12 - Writing High-Performance Swift Code: https://github.com/apple/swift/blob/master/docs/OptimizationTips.rst
-
Swift 性能探索和優化分析:
https://onevcat.com/2016/02/swift-performance/ -
Swift Concurrency Manifesto:
https://gist.github.com/lattner/31ed37682ef1576b16bca1432ea9f782 -
Ownership Manifesto:
https://github.com/apple/swift/blob/main/docs/OwnershipManifesto.md -
Swift性能優化分析:
https://juejin.cn/post/688786214412648449 -
Swift, C++ Performance:
http://www.primatelabs.com/blog/2014/12/swift-performance/
總結
以上是生活随笔為你收集整理的百度App Objective-C/Swift 组件化混编之路(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 百度App Objective-C/Sw
- 下一篇: 手把手教学电瓶车进电梯检测、多类别车辆追