歡迎大家前往雲+社區,獲取更多騰訊海量技術實踐干貨哦~
作者:sanhuazhang,此文發布在微信終端開發團隊的專欄
WCDB 作為微信的終端數據庫,從 2017.6 開源至今,共迭代了 5 個版本。我們一直關注開發者們的需求,並不斷優化性能,新增如全文搜索等常用的功能。而這其中,呼聲最高的莫過於 對 Swift 的支持。
WCDB ObjC 版本的實現中,由於引入了 C++ 代碼,並不能直接 bridge 到 Swift。因此,我們從 9 月份開始就着手使用原生的 Swift,重寫 WCDB。並於 10.10 和 11.8 分別在開發者群內發布了 alpha 和 beta 版進行測試。
今天,終於可以正式發布 WCDB Swift 的第一個正式版本了。
WCDB Swift 約有 1.5w 行代碼,使用 Pure Swift 編寫,幾乎不包含 Cocoa 的代碼。且與 ObjC 版保持完全一致的功能。
模型綁定
WCDB Swift 的模型綁定,基於 Swift 4.0 的 Codable
協議實現。通過建立 Swift 類型與數據庫表之間的映射關系,使得開發者可以通過類對象直接操作數據庫。
語言集成查詢
語言集成查詢深度結合了 Swift 和 SQL 的語法,使得純字符串的 SQL 可以以代碼的形式表達出來。結合代碼提示及糾錯,極大地提高開發效率。
同時,由於 Swift 的語法 比 Objective-C 更加簡潔,並有更強大的范型和類型推導,使得 WCDB 接口不僅更易編寫,而且更易讀易維護。
類似 Sample.Properties.identifier > 0
的語法,其返回值並不為 Bool
,而是語言集成查詢的 Expression
對象,WCDB 會根據這個語句,去進行 SQL 的查詢。同時,通過類型的定義,Swift 即可推導出 WCDB 查詢的結果為 Sample
類。
語言集成查詢同時內建了反注入機制,可以避免第三方從輸入框注入 SQL,進行預期之外的惡意操作。
深入 SQLite 源碼的性能優化
WCDB 基於 SQLite 開發,我們在之前的文章介紹過其對 SQLite 源碼進行的性能優化,以適配移動終端的場景。同樣地,這部分優化 Swift 版本也能享受到。
線程安全且並發
WCDB Swift 不僅可以安全地在任意線程進行數據庫操作,且其內部會智能地根據操作類型調配資源,使其能夠並發執行,進一步提升效率。
加密
基於 SQLCipher 的加密機制,可以為客戶端數據安全提供一定程度的保障。
字段升級
數據庫模型與類定義綁定,使得字段的增加、刪除、修改都與類變量的定義保持一致,不需要開發者額外地管理字段的版本。
模型綁定中新增了 newColumn
字段,該字段也會被自動創建到數據庫表中,開發者不需要手動管理。
全文搜索
WCDB Swift 提供簡單易用的全文搜索接口,並包含適配多種語言的分詞器,使得數據搜索更精准。
損壞修復
內建的修復工具可以在系統錯誤、磁盤故障等情況下,盡最大限度地將損壞的數據找回並導出。
Pure Swift
模型綁定對語言的依賴性很大。由於 ObjC 其強大的消息轉發機制,使得 WCDB 實現起來並沒有太大的問題。然而,動態性卻恰恰是 Swift 一直為人詬病的地方。
最省事的解決方案就是,直接引入 Cocoa,所有的問題都將不再是問題。然而,這並不是我們所期望的。
理性分析可以得出,一方面,全面的動態化會拖累 Swift 的性能,另一方面,這也會使得 Swift 的原生類型難以享受到模型綁定。
但我們的理由可能更感性一些 --- 情懷。稱之為強迫症也好,代碼潔癖也罷,Swift with Cocoa 總讓人心里有那么一絲別扭。因此,我們決定尋找 Swift 原生的解決方案。
WCDB 的模型綁定對語言有兩點依賴:
1. Accessor。ObjC 版本使用 selector
的 IMP
指針,使得 WCDB 可以獲取變量的值,並插入到數據庫中,或從數據庫中獲取數據寫入到變量。
2. 數據庫字段的映射。ObjC 版本使用宏定義,使得 WCDB 可以通過className.propertyName
的方式進行語言集成查詢的操作。
KeyPath
我們最初盯上的是 Swift 的 KeyPath
的機制,它通過 \
的語法,可以直接對變量進行讀寫操作,且語法上也與 className.propertyName
類似。
一個難題是,KeyPath
在不引入 Cocoa 的情況下,是並不提供 property 的名稱,這就無法通過 KeyPath
直接映射數據庫的字段。
Swift 也有一個相關的 SR 在討論這個問題。
顯然,我們不可能等待這個特性實現了再去做 WCDB Swift。因此我們嘗試使用“不常規”的方法,獲取到 KeyPath
對應的 property 名稱。
Mirror
是 Swift 里的反射類型,它可以遍歷每個變量,獲取其名稱和值,但不能對變量寫入數據。因此我們可以通過 KeyPath
對變量設一個獨一無二的特征值,然后再通過Mirror
遍歷變量,導出與特征值相同的 property 名稱。
這個“不常規”的用法在大部分情況下能夠生效,但對於 class
和 struct
相互嵌套的變量,容易因為內存混亂導致 crash。
Codable
在 KeyPath
的方案不夠完善的情況下,我們轉投了 Codable
協議。它是 Swift 4.0 新增的特性,本質是編譯前根據定義生成代碼,以完成序列化和反序列化的任務。
對應到 WCDB,將數據庫的字段讀寫到變量中,其本質就是一個序列化和反序列化的過程,而 CodingKeys
也可能可以用於語言集成查詢中的字段映射。
然而,由於這個特性還很新,還沒有太多文檔對其進行深入介紹,尤其是自定義 Encoder
和Decoder
這部分。
所幸的是,Swift 本身就是開源的。因此,我們參考 swift-corelibs-foundation 中的JSONEncoder
和 JSONDecoder
,實現了 TableEncoder
和 TableDecoder
,並通過CodingKeys
的定義,映射數據庫中的字段。
最終維護了我們對 Pure Swift 的堅持。
微信也轉向 Swift 開發了嗎?
相信這會是大家非常關心的問題。然而,很遺憾,目前還沒有。不僅微信,國內外大部分 app 都還沒有完全轉向Swift,但顯然這是個趨勢。
Google 在 11 月 fork 了 Swift。
大家猶豫不定的原因都大同小異:ABI不穩定,需要將二進制打包進去,增大app體積;某些方面性能還不夠好,而且現在多數是與 ObjC 混編,將進一步拉低性能 等等。
而這其中一個很重要的原因就是,Swift 的基礎設施還不完善,還難以支撐其大型 app 的開發。而 WCDB Swift 就是這類基礎設施之一。
因此,先有 WCDB Swift,未來才有用 Swift 編寫微信的可能,這邏輯沒毛病。
另一方面,沒有微信的上線機制的保護和龐大的用戶量的驗證,我們需要確保 WCDB Swift 的穩定性。因此,在 WCDB Swift 的第一版本,我們就提供了相對完善的測試用例,用例的代碼覆蓋率為 91.34%,能夠觸達絕大部分使用場景。
更多 WCDB Swift 的教程文檔、代碼樣例,包括源碼,直接到 Github 的 Tencent/wcdb 了解。
我們一起期待 Swift 成為開發者的首選的那一天。