歡迎大家前往雲+社群,獲取更多騰訊海量技術實踐乾貨哦~
作者: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 成為開發者的首選的那一天。