iOS 代碼使用 C+的zero-cost abstraction 特性
不少 iOS 項目里都有 C++ 代碼的痕迹,Objective-C 和 C++ 雖然都是 C 的 superset,但二者在語言特性上存在很大差異,Objective-C 的 runtime 使其語言的特性更豐富更易使用,但代價是會增加性能損耗以及編譯後的 binary size。
很多成熟項目開發到一定階段,會關注一些關鍵指標,比如 App size,現在超過 100 M 的 App 比比皆是,而 App Store 上超過 150 MB 的 App 只能通過 Wifi 下載,當常規的瘦身手段用盡之後,App size 每一個 MB 的減少都彌足珍貴,這篇文章向 iOS 開發者介紹 C++ 的 zero cost abstraction 特性,在特定的場景下使用能起到立竿見影的療效:減小 iOS App 的 binary size,給 App 瘦身。
zero-cost abstraction
Objective-C 和 C++ 同為面向對象語言,我們通過對象來抽象世界中的概念,但 Objective-C 的抽象伴隨著代價,抽象越多,定義的類越多,最後編譯出的 binary size 也就越大,而 C++ 卻沒這方面的煩惱,無論你定義多少類,設計多少 component,採用多少設計模式,並不會增加最後的 binary size,也就是所謂的 zero-cost,對 iOS 開發者來說,這種理論聽起來可能有些反常識,但如果你是先學習 C, C++,再接觸 Objective-C 的 runtime,理解起來再直白不過。
舉例來說,假設我們從伺服器收到一段請求 user 信息的 response,一般我們會將 response 還原成一個業務 model 對象,user 類的定義如下:
如果使用 C++ 來定義這個類,在 C++ 編譯器的眼裡,這個類的全部信息不過是兩個連續存在於內存空間上的 4 個位元組(假設一個 int 占 4 位元組)。我們訪問 user 對象的代碼:
會被編譯器翻譯成一段類似如下的內存訪問代碼:
在編譯器眼裡沒有類的概念,只有內存地址,偏移量,以及讀寫操作。即使我們加入更多的抽象,比如把 User 類放進 Car 類裡面,再把 Car 放進 City 類里,當我們使用 city->car->user->age 時,編譯器依舊會將代碼翻譯成直白的 memory access。
如果我們使用 Objective-C 來書寫上述代碼,情況就完全不一樣了,熟悉 Objective-C runtime 的同學明白接下來會發生一系列操作,編譯後的代碼里,Objective-C 的 runtime 會先嘗試給 user 對象發送 message(如果是通過 property 訪問),需要通過 user 對象的 isa 指針找到 User 類定義,再通過 selector 在 cache 里找到 IMP 地址,最後才從函數返回需要操作的目標內存地址。我只列出了關鍵的幾步,中間其實省略了 n 個流程,類越多,抽象的層次越多,步驟也就越多,這是由於 Objective-C 需要將 class 的定義編譯進最後的 binary 里,需要依賴 class 的信息來實現 runtime 的一些機制,class 越多,最後生成 binary 自然也就越大。
簡而言之,大部分編程語言和 Objective-C 類似,由於需要在 binary 中保存 class 的信息,而將抽象的成本帶入了編譯後的機器碼。通過上面的分析我們也不難發現 zero-cost abstraction 的好處體現在兩方面,一是 binary 更小,二是運行時更高效(沒有一層層的中轉)。
C++ 的 zero-cost 特性得益於編譯器的高效實現,我們在代碼里定義的所有類,最後都會被編譯器降維,高樓被夷為平地,信息卻不會丟失,編譯器用一片二向箔將面向對象的世界壓扁成一幅畫,畫里的機器碼仍然能嚴格準確的表達我們的意圖。
謹慎使用
使用 zero-cost abstraction 的代價即為使用 C++ 開發的代價,C++ 使用難度高於 Objective-C,過多引入 C++ 代碼可能會造成純 iOS 團隊的維護效率降低,在引入之前,最好有準確的評估和 demo 先行驗證。
By 「zero-abstraction」 I mean not a byte and not a cycle wasted compared to hand-crafted low-level alternatives. Often the overhead of a function call (and especially an indirect function call) is considered too much. Offering both hardware access and abstraction is the basis of C++. Doing it efficiently is what distinguishes it from other languages.
Bjarne Stroustrup
TAG:MrPeak雜貨鋪 |