淺談 Swift 應用架構設計

WeZZard
3 min readSep 4, 2019

數據模型的語義挑選

Swift 中提供兩種語義:value semantics 和 reference semantics。在 Swift 中,蘋果鼓勵使用 value semantics 而不是 reference semantics。

對於複雜數據結構而言,實現值語義可能是一個挑戰:因爲多數複雜數據結構都可能存在子葉節點修改後需要回溯到根節點或者父節點進行數據同步或者 sanity checking 的問題。對於某些情況,這個問題是可以解決的,對於某些情況,這個問題是無解的。

首先,我們要明確,value semantics 和複製是強綁定的一堆關係。而實現 value semantics 的複製策略中,copy-on-write 是最常用的一個。雖然這個策略已經不再適用本世紀初已經開始進入的多處理器時代,但是對於 GUI 編程和 UIKit 這種只在主線程進行渲染的框架而言,仍然是非常合適的選擇。

但是在實現 copy-on-write 策略的過程中,數據節點的引用不可以是雙向的:即雙向鏈表天然不可能實現 copy-on-write;樹型結構的節點不能存在指向 parent 的引用。

對於上述情況,一旦放棄 copy-on-write 實現複雜數據結構的 value semantics,那麼只剩下了全量複製。這對性能的衝擊是巨大的,還不如滾回 reference semantics + mutable/immutable objects 的範式。

但是在其他情況中,我們都可以通過 facade pattern 在數據結構的根部提供符合業務邏輯心智模型的接口,然後在這些接口內實現 sanity checking 和數據同步。

數據模型的線程同步

Apple 系操作系統中都提供了一個叫 libdispatch 的庫來進行線程同步。這個庫的好處就是同步過程不需要從用戶態掉入內核態,相較與傳統的加解鎖而言可以節省很多時間。在某一個 iOS 版本後,libdispatch 中的隊列不再與操作系統中的 thread 概念強掛鉤——也就是說,可能先後共有兩個隊列跑在一個 thread 上。在這種情況下,一些追求線程安全的數據結構在其內部使用 unfair lock 會是一種比較明智的選擇。

但是對於複雜數據結構配合 value semantics 而言,恐怕更需要的是 read-write lock。

首先要說明的是,copy-on-write 不是線程安全的——在兩個線程前後 hold 住一個 copy-on-write 的數據的時候,很有可能發生一個線程先開始寫,然後在寫入沒有完成前後面的線程開始複製,最後導致 data corruption。

對於這種情況,我們可以使用 read-write lock 解決。

數據模型的建立

在一個比較複雜的工具類 app 中,一份由用戶輸入來構造出來的數據結構往往可以被解析成幾種視角的數據——比如說一段文本就可以被解析成可以編輯的文本,以及用來高亮的 AST。

於是,對於某些業務單獨建立一個模型,然後從源數據模型轉換成目標業務的模型會成爲一個比較可以被科學管理的途徑。

當模型被建立後,如何將現有的模型轉換成目標業務模型又會變成一個不小的挑戰。我個人比較傾向於使用編譯器中 AST 優化器的結構做一個數據模型 builder,且我們可以最終將 building 算法優化成 online 的。

--

--