ios元件化通訊

語言: CN / TW / HK

隨著專案越來越複雜,人員越來越多,開發人員勢必要面臨元件化的問題,關於這個問題,有機會專門討論下,此篇文章裡只做簡單涉及,重點在於元件化之後的通訊問題

元件化方式

pod lib create xxxxModule

pod會自動讀取rep模板建立,期間會需要做一些偏好設定,很常規的操作

偏好可以選擇建立example

建立好之後,xxxModule/xxxModule目錄下產生兩個Assets和Class兩個目錄

  • Assets 放置資源

  • Class 放置你的程式碼

程式碼跟資放置好之後,進入Example 執行pod install,Example依賴Module就會自動安裝了

Xcode導航檢視結構裡

  • Module存在於 Pods/Development Pods/xxxModule

  • xxxModule/Pod/IFLHomeModule.podspec 配置module版本,rep地址,以及三方依賴

  • Pods/Pods 主工程Example pod依賴 及 xxxModule pod s.dependency 都會安裝在Pod/Pods

  • 資原始檔存放於 Pods/Development Pods/xxxModule/**

  • module裡資原始檔的訪問

    • Pods/Development Pods/xxxModule/Pod/xxxModule.podspec 配置s.resource_buneles

    • [[NSBundle bundleForClass:[self class]].resourcePath stringByAppendingPathComponent:@"/xxxModule.bundle"]

元件策略一: target - action (CTMediator)

簡單整理了下CTMediator,實現機制很簡單

image.png

應用中的業務想要訪問元件,都通過CTMediator獲取目標檢視,提供自己需要的元件target名稱,及get名,也就是兩個靜態的字串,可能是巨集或者全域性配置變數

CTMediator 通過目標target,根據action,通過繫結的元件拿到目標檢視,完成初始化,返回給呼叫者。action與目標元件中的檢視都是靜態配置好的

其實專案程式碼層面沒有什麼問題,但元件並不是一成不變的,可能會面臨多次升級,這種設計不可避免的,每次元件更新,除非元件根業務結構不發生變化,否則每次target也得調整,也就是元件與target是密不可分的

元件業務如果不是很複雜,不一定需要處理成元件,一般我面臨到這種情況就是業務龐雜到影響專案協作,如果編譯依賴度過高,而且效率低下,我會毫不猶豫選擇元件化處理

這種target-action 方式不一定是處理元件通訊,業務模組中也可以選擇處理,這樣能一定程度減輕自己程式碼的業務侵入,target相當於做了一層業務隔離,這有助於穩定自己的業務架構設計,同時對程式碼也起到一定規範作用

如果要進一步解決耦合依賴問題,不只業務層面,還要考慮編譯層面的效率問題,target action 可能會變得越來越繁雜,分層也會變得越來越模糊,而且需要寫很多費解的target設計,個人感覺不是那麼的好用,所以我就果斷選擇BeeHive,不只解決了元件通訊問題,更是一解耦利器 關鍵是協議設計很明瞭

元件策略二: BeeHive

為了深刻了解BeeHive,還是從原始碼入手追蹤一遍,最後再總結

通過service獲取目標例項物件

image.png

這個流程的目的是通過service獲取到類例項,程式碼裡最直接的體現就是 原始碼coding層面不用再import了,這是一種最直接的解耦方式了

以上流程圖中涉及兩個字典查詢

  • BHContext::servicesByName

  • BHServiceManager::allServicesDict

既然涉及BHContext,不妨看下BHContext,究竟BHContext是什麼

跳過具體屬性不看,看方法

image.png

BHContext主要就是 關於例項的增刪查 簡單瞭解BHcontext就是存例項 取例項,就是快取了, 因為現在是分析原始碼解耦機制,細節我們暫可省略

再檢視下 BHServiceManager::allServicesDict這個字典

image.png

可以瞭解 allServicesDict 是用來註冊service - class 鍵值資訊的,兩種方式,一種讀取檔案,一種動態註冊

現在可以知道了,manager負責註冊類資訊,context負責快取類資訊

繼續溯源

image.png

image.png

image.png

也就是說BeeHive在初始化階段就完成 service - class 的靜態註冊

同時 BeeHive初始化 也需要完成 localModules裝載 + modules註冊

分析staticModules裝載

image.png

module靜態載入其實 是載入plist記錄的 一些module類的資訊

這裡兩個小細節值得關注一下

image.png

裝載的module 有優先順序

image.png

  • 還有就是裝載modules時不能重複,如果通過快取的modules資料來源處理後續註冊類邏輯的話,可能就會觸發註冊異常了 同樣 相同的module,優先順序也可能衝突

  • 還有一點更重更要:

    • 既然是初始化 讀取modules plist資原始檔,何來重複之說呢?

    • app dyld載入類階段,也就是靜態裝載 plist資源modules之前, load會在此之前執行,load裡存在 registerDynamicModule 操作,此處又個印象就行,回頭會就這塊分析說明

    • 還有一點猜測:目前的分析階段,我們是單個BeeHive元件,而如果多個元件呢,其他的plist

      資原始檔呢,而modules註冊快取只能是一份

由此可以看出開源作者很細節,這是頂層設計的結果,在我們在設計專案架構時,作為一名開發人員,這很值得參考

靜態註冊modules class 資訊快取在 BHModuleaManager::BHModuleInfos

BHModuleInfos 是個 NSMutableArray

BHModuleManager::registedAllModules - modules註冊

  1. 首先要對快取的modules排序 (兩層規則)

  2. level值大的排前面

  3. level相同,priority值小的,優先順序高, 排前面

也就是說 module註冊按照分組進行,組有優先順序順序; 組內的module同樣也具有優先順序順序

這裡我嗅到了些許 dl類載入的氣息

  1. 其次從快取的modules裡順序取出module 字串,獲取module類,並執行初始化獲取module物件

BHModuleManager::BHModules 快取所有的module類的物件

  1. BHModuleManager::registerAllSystemEvents註冊所有系統事件

至此 module類例項物件快取完成

image.png

可原始碼線性分析流程中斷了

沒關係 我們全域性搜就好了

modSetup modInit

image.png

繼續搜尋

image.png

既然BHModuleManager快取了一個大字典,Key:列舉事件, Value: selector, 那肯定是有專門處理事件的地兒,我們接著搜

image.png

image.png

而當前appDelegate 繼承 BHAppDelegate, BeeHive配置及一些初始化在前,app launch在後,這樣之前的斷層就續上了

繼續追蹤原始碼

image.png

image.png

我們知道了,application launch之前,所有快取的module執行 modSetup modInit

modSetup -> BHServiceManager::registerService

每個module類其實繫結一個業務類,而modSetup就是通過 allServicesDict字典快取業務類 
<Protocol:Class>

原始碼層面,BeeHivew解耦意圖及原理實現,我們大概有了初步明確認知,接下來就是一些細節知識補充了

BeeHive監聽app生命週期事件

image.png

image.png

BeeHive已經對一些基本事件做了管理,你只需要在你的Module類裡 實現相應的事件回撥協議方法就好了

當然自己可以擴充套件 module協議 及自定義事件

BeeHive總結

BeeHive可以分為

  • BeeHive核心

  • BHContext

  • Service註冊

  • Module註冊

  • Modules

  • BHContext - service列表,module列表,編譯引數

  • BeeHive核心負責service module的註冊,模組之間排程

  • Modules其實是一個邏輯模組集合,各個module均通過BeeHive核心排程