Bo2SS

Bo2SS

2 應用開發篇(上)

好久沒有《帶你領略 iOS 知識體系的全貌》了,現在開始一個新的篇章 —— 應用開發篇。在這一篇,我們會講到 iOS 開發中的GUI 框架、響應式框架、動畫、A/B 方案以及消息總線

image

前文推薦

如果你錯過了第一個篇章 —— 基礎篇,可以從這裡跳轉:

《帶你領略 iOS 知識體系的全貌》基礎篇(上)

《帶你領略 iOS 知識體系的全貌》基礎篇(中)

《帶你領略 iOS 知識體系的全貌》基礎篇(下)

《帶你領略 iOS 知識體系的全貌》基礎篇(最佳學習路徑)

21 | 除了 Cocoa,iOS 還可以用哪些 GUI 框架開發?#

優化 App 的啟動速度,除了可以從主線程上入手外,還可以考慮 GUI(Graphical User Interface,圖形用戶界面)上的優化。

目前流行的 GUI 框架#

現在流行的 GUI 框架除了 Cocoa Touch 外,還有 Texture(原名 AsyncDisplayKit)、 WebKit、React Native(Facebook)以及 Flutter(Google),它們的對比如下:

GUI 框架Cocoa TouchTextureWebKitReact NativeFlutter
Platform supportiOSiOSiOS、AndroidiOS、AndroidiOS、Android
Programming languageOC、SwiftOC、SwiftJavaScriptJavaScriptDart
Render engineCoreAnimationCoreAnimationWebCoreNativeSkia
Layout methodFrame、Auto LayoutFlexBoxFrame、FlexBoxFrame、FlexBoxFrame、FlexBox、LayoutWidgets
  • FlexBox 布局可以讓 iOS 開發者用到前端先進的 W3C 標準響應式布局,iOS 新推出的 UIStackView 布局方式,也是按照 FlexBox 布局思路來設計的。Cocoa Touch 框架本身不支持 FlexBox 布局,但是通過 Facebook 的 Yoga 庫也能夠使用 FlexBox 布局。

  • Texture 框架的基本單元是基於 UIView 抽象的節點 ASDisplayNode。相比 UIView,ASDisplayNode 是線程安全的,可以在後台線程上並行實例化和配置整個層級結構。正因為 Texture 使用了異步節點計算,所以它可以提高主線程的響應速度。

  • 在 iOS 開發中,我們最常使用的 UIWebView 和 WKWebView 控件都是基於 WebKit 框架。關於 WebKit 框架,作者曾寫過一篇博客 “深入剖析 WebKit”,詳細分析了它的原理。

  • React Native 和 Flutter 框架渲染的相關介紹,後面的原生與前端共舞篇會詳細介紹。

GUI 框架裡都有哪些?#

控件、渲染樹、渲染層樹。

  • 控件主要負責界面元素數據的存儲和更新;

  • 渲染樹這種抽象的樹結構主要記錄控件之間的關係;

  • 渲染層樹由渲染層對象組成,渲染層對象是根據 GUI 框架的優化條件來確定創建的,它記錄包含了哪些控件,結合渲染樹佈局可以生成 Bitmap,最終供 GPU 來渲染。

它們之間的關係圖如下:

控件、渲染樹、渲染層樹之間的關係 ——《極客時間》

其它

  • 使用 WebKit 的網頁顯示慢,不是因為它的渲染性能不如其他框架,而主要是由於加載 CSS 和 JavaScript 資源的方式導致的。另外,解析 HTML、CSS、JavaScript 時需要兼容舊版本,JavaScript 類型推斷失敗會重來,列表缺少重用機制等原因,也導致 WebKit 框架的整體性能沒有其他框架好。

  • Flutter 本來也是基於 Chrome 瀏覽器引擎的。後來,考慮到 Flutter 的性能,谷歌去掉了它對 HTML、CSS、JavaScript 的支持,改用自家的 Dart 語言以甩掉歷史包袱,具體的細節可以查看採訪 Flutter 創始人 Eric 的視頻—— 知乎。

渲染流程#

GUI 框架中的渲染技術一直很穩定,一般都會經過佈局、渲染、合成這三個階段。

  • 佈局階段:主要依據渲染樹計算出控件的大小和位置。

  • 渲染階段:主要是利用圖形函數計算出界面的內容。一般情況下,對於 2D 平面的渲染都是使用 CPU 計算,對 3D 空間的渲染會使用 GPU 計算。

  • 合成階段:主要是合併圖層,節省顯示內存。

Texture 裡 Node 的異步繪製

對於那些希望能夠在用戶交互體驗上進行大幅提升的 iOS 開發者來說,Texture 具有很小的切換成本和大幅提升的性能收益。

其優勢就是:

  1. 開發了線程安全的 ASDisplayNode;

  2. 能夠很好地和 UIView 共生。

異步繪製原理:

ASDisplayLayer (CALayer 的封裝)是整個繪製的起點,繪製事件先在 displayBlock 設置好,然後 ASDisplayNode (替代了 CALayer 裡的 delegate)調用 displayBlock 來進行異步繪製。因為 displayBlock 用的是線程安全的 Core Graphics,所以可以把 displayBlock 放到後台線程去異步執行。

22 | 細說 iOS 響應式框架變遷#

響應式框架介紹#

定義:能夠支持響應式編程範式的框架。

特點:使用響應式框架,在編程時就可以使用數據流傳播數據的變化,響應這個數據流的計算模型會自動計算出新的值,然後將新的值通過數據流傳給下一個響應的計算模型,如此反復下去,直到沒有響應者為止。

現狀:iOS 響應式框架有 ReactiveCocoa(簡稱 RAC)、RxSwift,但是都沒有流行起來,直到前端推出 React.js 後,響應式思路才遍地開花。

為什麼 ReactiveCocoa 在 iOS 原生開發中就沒流行起來呢?

帶著問題往下看#

ReactiveCocoa 框架的思路,與 React.js 的思路基本是一致的。那我們就來看看 React.js 框架做了些什麼?關鍵在於增加了虛擬文檔對象模型(Virtual DOM)。

React.js 框架的底層有個 Virtual DOM,它與頁面組件狀態綁定,和 DOM(文檔對象模型)之間存在映射與轉換關係。其渲染原理如下圖所示:

React.js 框架渲染原理 ——《極客時間》

可以看出

  • React.js 框架先操作 Virtual DOM,此時並不會直接進行 DOM 渲染,而是在完成了 Diff 計算得到所有實際變化的節點後,再進行一次 DOM 操作,然後整體渲染。Virtual DOM 相當於 JavaScript 和 DOM 之間的一個緩存。

  • 而不像 JavaScript 每次操作 DOM,就會全部重新渲染,性能損耗很大。


回到問題:為什麼 ReactiveCocoa 在 iOS 原生開發中就沒流行起來呢?

對於前端來說,DOM 樹的結構非常複雜,進行一次完整的 DOM 樹變更,會帶來嚴重的性能問題,Virtual DOM 可以很好解決這個問題。

而對於 iOS 原生的 Cocoa Touch 框架來說:

  • 這種性能問題並不存在,其界面節點樹結構比 DOM 樹簡單得多;

  • 並且,其渲染機制與前端也不同,Cocoa Touch 每次更新視圖時不會立刻進行整個視圖節點樹的重新渲染,而是會通過 setNeedsLayout 方法先標記該視圖需要重新佈局,直到繪圖循環到這個視圖節點時才開始調用 layoutSubviews 方法進行重新佈局,最後再渲染。

所以,ReactiveCocoa 框架並沒有為 iOS 的 App 帶來更好的性能。當一個框架可有可無,而且沒有明顯收益時,一般團隊是沒有理由去使用的。


其它:ReactiveCocoa 裡面也有很多值得我們學習的地方,比如:

23 | 如何構造酷炫的動畫效果?#

業界痛點#

  • 手動編寫動畫的代碼非常複雜,很多動畫細節的調整需要和動畫設計師不斷溝通打磨;

  • iOS、Android、Web 各平台開發者需要各自維護動畫代碼。

發問:有什麼辦法能夠將動畫製作和編程開發隔離開,專人做專事,並且多平台動畫效果保持一致呢?

Lottie#

有的,它就是 Lottie 框架,Airbnb 開源的一個動畫框架。

使用步驟

  1. 動畫設計師使用After Effects製作動畫,然後通過 Bodymovin 插件將動畫導成 JSON 文件;

  2. 開發者使用 Lottie 加載和渲染這個 JSON 文件,從而自動轉換成對應的動畫代碼。


實現原理

Lottie 在 iOS 內做的事情就是:

1)將 JSON 文件(After Effects 製作動畫生成的中間媒介)的內容一一映射到 iOS 中 LayerModel、Keyframe、ShapeItem、DashElement、Marker、Mask、Transform 這些類的屬性中,保存;

2)再通過 CoreAnimation 進行渲染。

所以,Lottie 實際上自動化了動畫設計文件轉化為開發代碼的過程,把設置 LayerModel 等屬性的任務交給了:JSON 文件和 Lottie 映射規則。


💡Tips

  1. Lottie 這樣的工作流程或許就是未來的趨勢,就像 iOS 現在的發展趨勢一樣,越來越多的業務邏輯不再需要全部使用 Objective-C 或 Swift 來實現了,而是使用 JavaScript 語言或者 DSL 甚至是工具來描述業務,然後將描述業務的代碼轉換成一種中間代碼,比如 JSON,不同平台再對相同的中間代碼進行解析處理,以執行中間代碼描述的業務邏輯。

  2. Lottie 詳細的說明和使用示例代碼,可以參考 Lottie 官方 iOS 教程。Lottie 不僅支持物理效果,還支持頁面切換的過場動畫。

  3. 沒有動畫設計師配合的開發者,可以去 LottieFiles 上看看,這是一個動畫設計師分享作品的平台,每個動畫效果的 JSON 文件都可下載使用。

作者:Okiri George

24 | A/B 測試:驗證決策效果的利器#

A/B 測試定義#

A/B 測試,也叫桶測試或分流測試,指的是針對一個變量的兩個版本 A 和 B,測試用戶的不同反應,從而判斷出哪個版本更有效,類似統計學領域使用的雙樣本假設測試。

簡單地說,A/B 測試就是檢查不同的 App 用戶在使用不同版本的功能時,哪個版本的用戶反饋最好。

App 開發中的 A/B 測試

在 App 版本迭代中,我們可以把舊版本理解為 A/B 測試裡的 A 版本,把新版本理解為 B 版本。兩個版本同時存在,B 版本一開始是將小部分用戶放到 B 測試桶裡,逐步擴大用戶範圍,通過分析 A 版本和 B 版本的數據,看哪個版本更接近期望的目標,最終確定用哪個版本。

總的來說,A/B 測試是以數據驅動的可回退的灰度方案,客觀、安全、風險小,是一種成熟的試錯機制。

A/B 測試全景設計#

一個 A/B 測試框架主要包括三部分,結構圖如下:

A/B 測試方案的結構圖 ——《極客時間》

  • 策略服務,為策略制定者提供策略,包含決策流程和策略維度。

    • 一般由服務端提供,方便服務端隨時根據用戶群的維度分布分配測試桶。
  • A/B 測試 SDK,集成在客戶端內,用來控制上層業務走不同的策略。

    • 推薦:SkyLab,作者 Mattt 也是我們熟悉的 AFNetworking 網絡庫和 Alamofire 網絡庫的作者,該庫在接口設計上也是使用 block 來接收 A/B 版本的區別處理,易用性很高,值得學習。

    • 生效機制:如果一個策略只在一個地方生效的話,可以使用熱啟動生效機制;而如果一個策略在多個地方生效的話,最好使用冷啟動生效機制。

  • 日誌系統,負責反饋策略結果供分析人員分析不同策略執行的結果。

    • 一般由服務端提供。

25 | 怎樣構建底層的發布和訂閱事件總線?#

事件總線定義#

事件總線是對發布和訂閱設計模式的一種實現,通過發布、訂閱可以將組件間一對一和一對多的耦合關係解開。

這種設計模式,特別適合數據層通過異步發布數據的方式告知 UI 層訂閱者,使得 UI 層和數據層可以不用耦合在一起,在重構數據層或者 UI 層時不影響業務層。

iOS 裡已有的相關技術#

  • Block 和 Delegate。只適合一對一的模式,如果需要不斷異步發布給下一個數據訂閱者的話(消息之間有因果關係),會出現回調嵌套其他回調的情況。

  • KVO 和 NSNotificationCenter。它們支持一對多的模式。但是使用 KVO 是強依賴屬性的,只要更新了屬性就會發布給所有的觀察者,對應關係過於靈活,難以管控和維護;使用 NSNotificationCenter 也有類似的問題,通過字符串來維護發布者和訂閱者之間的關係,不僅可讀性差,而且和 KVO 一樣面臨著難以管控和維護的情況。


Q:那麼有沒有好的第三方庫可以處理事件總線呢?

其實,前面提到的響應式第三方庫 ReactiveCocoa 和 RxSwift 對事件總線的支持都是沒有問題的,但這兩個庫更側重的是響應式編程,事件總線只是其中很小的一部分。所以,使用它們就有點大材小用了。

而現在前端領域有一種模式叫作 Promise,這是一種專門針對異步數據操作編寫的一套統一規則的模式。

Promise#

本質上,這種模式是通過 Promise 對象保存異步數據操作,同時 Promise 對象提供統一的異步數據操作事件處理的接口。

Promise 對象會有三種狀態

  1. pending:異步事件正在等待處理;

  2. fulfilled:異步事件成功完成;

  3. rejected:異步事件沒有成功完成。


還有兩個重要的方法:then 和 catch。

Promise 對象每次執行完 then 或者 catch 方法後,都会返回先前的 Promise 對象,同時該 Promise 對象的狀態會根據異步操作的結果而改變。

  • then:執行 then 方法對應訂閱操作,Promise 對象觸發 then 方法對應發布操作。then 方法執行完返回 Promise 對象,並能夠繼續同步執行多個 then 方法,由此,實現了一個發布操作對應多個訂閱事件。

  • catch:如果執行 then 方法後返回的 Promise 對象是 rejected 狀態的話,程序會直接執行 catch 方法。


Q:那麼 iOS 中如何使用 Promise 模式呢

引入 PromiseKit(Homebrew 的作者 Max Howell 開發)。

此外,PromiseKit 還為蘋果的 API 提供了擴展,如 UIKit、Foundation、CoreLocation、QuartzCore、CloudKit 等等,甚至還支持了第三方的框架 Alamofire,具體可參考 PromiseKit Organization

通過簡單、清晰、規範的 Promise 接口將異步的數據獲取、業務邏輯、界面串起來,對於日後的維護或重構都會容易很多,趕快去試試吧~

好了,今天的分享就到這裡~下次我們會進入《帶你領略 iOS 知識體系的全貌》應用開發篇(下),涉及 JSON 處理、佈局框架、富文本、TDD/BDD 和編碼規範等相關內容,別忘了關注,咱們下期見!

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。