成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

設計一個簡單的iOS架構

移動開發
正如“100個讀者就有100個哈姆雷特”一樣,對于架構的理解不同的軟件工程師有不同的看法。架構設計往往是一個權衡的過程,每一個架構設計者都要考慮到各個因素,比如團隊成員的技術水平、具體的業務場景、項目的成長階段和開發周期。

前言

正如“100個讀者就有100個哈姆雷特”一樣,對于架構的理解不同的軟件工程師有不同的看法。架構設計往往是一個權衡的過程,每一個架構設計者都要考慮到各個因素,比如團隊成員的技術水平、具體的業務場景、項目的成長階段和開發周期。

本文談談筆者的一些架構理念,以及本人是如何設計一個簡單的 iOS 架構。

iOS 架構 DEMO

一、關于組件化

組件化似乎是項目發展壯大過后必然要做的事情,它能讓各個業務線的工程師不需要過多的關注其他業務線的代碼,有效的提高團隊整體效率。然而實施組件化的時機是在需求相對穩定、產品閉環形成過后。所以本文不會應用組件化,但是這里簡單談談業界的組件化方案。

組件化的核心問題就是組件間如何通訊。“軟件工程的一切問題都能通過一個間接的中間層解決。”中介模式很自然的運用起來:

設計一個簡單的iOS架構

這樣雖然能統一組件間的通訊請求,但是卻沒有避免 Mediator 和目標組件的耦合,ModuleA 工程中仍然需要導入 ModuleB 。

所以重點問題落在了解耦上: 

設計一個簡單的iOS架構

要達到 Mediator 和目標組件的解耦,就需要實現它們之間的間接調用(圖中虛線),既然是間接調用,必然需要一種映射機制。在 iOS 開發中,業界大概有三種方式來處理。

(1) 使用 URL -> Block 解耦

簡單來說就是將組件的調用代碼放入 block 中,然后 URL 作為 key,block 作為 value,存入一個全局的 hash 容器,組件通過一個 URL (比如 "native/id=10/type=1" )向 Mediator 發起請求,Mediator 找到對應的代碼塊執行。由此,解開了 Mediator 和目標組件的耦合(見博客:蘑菇街 App 的組件化之路)。

這種方案的缺陷很多:組件越多常駐內存越多;解析 URL 邏輯復雜;URL 無法表述具體語言相關的對象類型。所以這種方式并不適合組件化解耦。

(2) 使用 Protocol 解耦

阿里的 BeeHive 是該方案的很好實踐,筆者閱讀了一下源碼,它的大致工作原理如下:注冊 Protocol 對應的組件,這個和上面說的 URL->Block 方式如出一轍,只不過這里是 Protocol-> Module ;組件申請訪問時導入對應的 Protocol 通過 Mediator 獲取到對應的組件對象。由于協議的表述能支持所有的對象類型,所以這種方式能基本解決組件間通信的需求。

BeeHive 注冊組件有幾種方式,一種是監聽了動態鏈接時 image 二進制文件加載完成的回調,通過修改代碼段的方式判斷對應的模塊進行注冊;第二種是在 +load 方法里面注冊;第三種是異步注冊,但是這種方式存在一個問題,可能組件使用方準備使用組件的時候,這個組件還未注冊成功。

BeeHive 還為組件設置了優先級的概念,它通過數組來保持優先級排序,在源碼中能看到一些數組排序的邏輯,這就帶來了相當多的高時間復雜度的運算。

所以,組件數量過多的話,會延長動態鏈接庫的過程。

BeeHive 為了讓每一個組件享有獨自的 app 生命周期、3D touch 等功能,會將這些系統級的事件發送給每一個組件,且不談大量的方法調用損耗,它必須讓入口文件 AppDelegate 繼承自 BeeHive 的 BHAppDelegate,筆者感覺侵入性過強,并且當開發者需要復寫 AppDelegate 方法的時候,還要注意讓super調用一下,可以說很不優雅了。

在基于協議的組件化方案中,組件使用方能直接拿到目標組件的實例,那么使用者可能對該實例進行修改,這可能會帶來安全問題。

(3) 使用 Target-Action 解耦

Casa Taloyum 前輩的 iOS應用架構談 組件化方案 為此做出了***實踐。

Mediator 使用 Target-Action 來間接的調用目標組件,無需專門注冊。組件維護者需要做一個 Mediator 的分類,通過硬編碼調用目標組件,然后組件使用者只需要依賴這個分類就行了。

封裝的 Mediator 源碼只有簡單的 200+ 行代碼,并且很易懂。這也讓開發者能對組件化的實施更加有信心,不會因為基礎設施的錯誤而束手無策。

小總結

關于以上組件化的簡單表述僅代表筆者的個人見解,由于筆者并沒有真正的實施組件化,所以理解可能有誤。

雖然筆者設計的 iOS 架構不會應用組件化,但是這給我們的架構設計帶來了前瞻性的引導,這非常重要。

二、模塊化思維劃分文件

在團隊開發中,項目發展到后期總是會出現某些文件或代碼難以管理,出現這種情況的主要原因通常是項目開發過程中對文件的管理過于隨意。

開發者應該盡量將所有代碼文件歸于模塊,而不要出現模擬兩可的文件。而筆者這里說的模塊,是有具體意義的模塊,比如圖片處理模塊、字體處理模塊,而不是諸如 Public、Common 等無具體意義的代碼文件。

試想,在多人開發中,當所有人都覺得有些代碼不知道怎么歸類的時候,就會往 Public 里面扔。當你某天想要整理一下這個 Public,會發現已經無從下手;或者當你需要遷移項目中的某個業務模塊時,會附帶遷移一些模塊,當這個模塊是有意義的(比如圖片處理模塊),你的遷移成本會非常低,但是當這個藕斷絲連的模塊是 Public 時,時間成本可能高于你的想象,估計你會將它完整的拷貝過去,而又對新項目造成了污染。

全局的公共文件是產生垃圾代碼的源頭。筆者認為幾乎所有的代碼都是可以歸類為模塊的。

大致梳理了一個文件分類,當然這個分類是靈活的,只是要分模塊劃分:

  • - GeneralModules 放項目獨有的通用配置模塊(比如通用顏色模塊、通用字體模塊)
  • - ToolModules 放工具類模塊(比如系統信息模塊)
  • - PackageModules 放基于業務的一些封裝(比如提示框模塊、加載菊花模塊)
  • - BusinessModules 放業務模塊(比如購物車、個人中心)

具體里面放了些什么,可以查看筆者的 DEMO。

三、減少全局宏的使用

很多時候,過多的宏讓項目很不整潔,每一個開發者都往全局文件添加宏,而往往只是一段簡單的代碼,筆者認為開發中應該盡量少使用宏,原因如下:

  • 宏在預編譯階段替換為實際代碼,存在效率問題
  • 使用宏的地方可能只需要一塊內存,但是宏替換過后開辟了多個(這種情況應該用常量替換宏)
  • 可能存在潛在的宏命名沖突
  • 宏包裝過多的代碼難以理解和調試
  • 代碼遷移時需要處理全局的宏

實際上,非得使用宏的地方并非那么多,比如需要定義一個全局的導航欄字體方便使用,可以將通用字體的配置參數作為一個模塊: 

  1. @interface YBGeneralFont : NSObject 
  2. /** 導航欄標題字體 */ 
  3. + (UIFont *)navigationBarTitleFont; 
  4. @end 

或者用常量來代替宏: 

  1. .h 
  2. FOUNDATION_EXTERN NSString * const kNotify_xxx; //xxx通知 key 
  3. .m 
  4. NSString * const kNotify_xxx = @"kNotify_xxx"

這么做也便于轉換思維,畢竟 swift 中是沒有宏的。

四、去基類化設計

代碼設計中,應該盡量避免基類的使用,也就是說,你不應該總是要求開發者去繼承你的基類來做功能。使用基類將造成不可避免的耦合,為業務的長期發展帶來阻礙(當然某些情況是可以使用基類的)。

其實使用基類就算了,若是將大量的業務邏輯放入基類中將是災難的開端。試想,當項目新成員一來就看見成千上萬行的基類代碼TA作何感想?

另外一種場景,當需要將項目中的某個模塊遷移到其他項目,或者需要將其他項目合并入當前項目,基類的合并將是一個非常頭疼的問題,它藕斷絲連的模塊和代碼會讓你抓狂。

那么,類的工具方法應該放哪兒?對所有類的統一配置應該放哪兒?對封裝模塊的個性化定制應該怎么做?

裝飾模式

類的工具方法,按道理說可以提取為模塊,但是有些場景可能顯得不夠簡潔。

其實只要留意 iOS 官方的 API,你就不難發現裝飾模式的大量應用,使用數個分類將大量的方法按照功能分類,會清晰且優雅: 

  1. @interface UIViewController (YBGeneral) 
  2. /** 基礎配置 */ 
  3. - (void)YBGeneral_baseConfig; 
  4. @end 
  5. @interface UIViewController (YBGeneralBackItem) 
  6. /** 配置通用系統導航欄返回按鈕 */ 
  7. - (void)YBGeneral_configBackItem; 
  8. /** 重寫該方法以自定義系統導航欄返回按鈕點擊事件 */ 
  9. - (void)YBGeneral_clickBackItem:(UIBarButtonItem *)item; 
  10. @end 

不過要注意的時,定義分類的時候一定要加一個前綴標識以避免方法覆蓋。

AOP

面向切面編程在 iOS 領域經典的應用就是利用 Runtime 去 Hook 方法: 

  1. @implementation UIViewController (YBGeneralHook) 
  2. + (void)load { 
  3.     [self YBGeneralHook_exchangeImplementationsWithOriginSel:@selector(viewDidLoad) customSel:@selector(YBGeneralHook_viewDidLoad)]; 
  4. + (void)YBGeneralHook_exchangeImplementationsWithOriginSel:(SEL)originSel customSel:(SEL)customSel { 
  5.     Method origin = class_getInstanceMethod(self, originSel); 
  6.     Method custom = class_getInstanceMethod(self, customSel); 
  7.     if (origin && custom) { 
  8.         method_exchangeImplementations(origin, custom); 
  9.     } 
  10. - (void)YBGeneralHook_viewDidLoad { 
  11.     NSLog(@"進入:%@", self); 
  12.     [self YBGeneral_baseConfig]; 
  13.     if (self.navigationController && [self.navigationController.viewControllers indexOfObject:self] != 0) { 
  14.         [self YBGeneral_configBackItem]; 
  15.     } 
  16.     [self YBGeneralHook_viewDidLoad]; 
  17. @end 

代碼中統一配置了 UIViewController 的系統導航欄返回按鈕,注意這里調用的業務配置方法都是定義在 UIViewController 的分類里面的。若有某些導航欄需要格外配置返回按鈕的需求,可以拓展一個屬性來控制。

面向協議設計模式

對于一些封裝的組件,多考慮使用協議來個性化定制,繼承作為最差方案,而非是***方案。

定義一個遵守組件定制協議的屬性是常用的解決方法:

  1. @property (nonatomic, strong) id<someProtocol>  strategy; 

不同的屬性作為不同的策略,組件內部通過調用對應的協議方法實現個性化定制。而當使用者想要改變策略時,只需要更改這個屬性就行了。面向協議設計模式結合策略模式是一個很好的實踐。

五、MVC?MVP?MVVM?VIPER?

業務具體的架構模式是個讓很多開發者頭疼的問題,因為有時候能讓復雜業務更清晰,有時候卻因為膠水代碼過多而臃腫。

實際上為什么要嚴格的遵守架構模式呢?為什么每一個業務模塊的架構模式都要一模一樣呢?

筆者認為正確的架構思路一定是根據業務來的,不同的模塊,不同的業務線完全可以有不同的架構,只需要架構足夠清晰不至于晦澀。

大致設計了一下架構的主旋律:

設計一個簡單的iOS架構

  • DataCenter 負責數據的獲取、處理、緩存等。
  • Model 設計為“瘦” Model,便于復用和遷移;也考慮到數據源可能數量龐大,若 Model 設計得過于“胖”,會造成更多的內存占用。
  • View 負責數據的展示,可以根據業務情況權衡是否需要 ViewModel 處理界面邏輯。
  • ViewController 作為 DataCenter 和 View 的橋梁。

筆者設計的項目目前不會很復雜,多數情況上面的架構就已經夠用,若某個頁面功能過多,完全可以提取一些額外的模塊,比如 DataCenter 處理過于復雜,那就把數據的處理和緩存提取出來:xxxDataProcesser、xxxDataCache。這些都是靈活的,只需要按照模塊化的思維提取,ViewController 的代碼相信也不會太多。

關于響應式框架

Reactivecocoa 雖然強大,筆者以前也用過,不過它是一個重量級框架,學習成本有點高,可能會因為團隊成員對其了解不足導致難以定位的錯誤。

而美團的 EasyReact 似乎是一個福音,筆者大概瀏覽了一下源碼,質量確實很高,對性能方面的處理很精致,基于圖論算法的處理也感覺很棒,項目侵入性也很小。不過缺點就是太新了,需要開發社區一定時間的驗證,暫時筆者持觀望態度。

結語

本文只是作者思考過后對一個項目架構的簡單設計,還有很多部分需要完善和補充,具體細節也可能會按照具體情況修改。Demo 只是一個雛形,希望和各位讀者朋友能有所交流。

 

責任編輯:未麗燕 來源: 簡書
相關推薦

2025-05-27 10:15:00

Go開發軟件架構

2024-04-24 10:38:22

2019-06-27 09:50:49

高性能秒殺系統

2012-02-01 14:12:55

iOS本地緩存機制

2020-11-11 09:49:12

計算架構

2021-05-20 13:22:31

架構運維技術

2011-03-24 09:34:41

SPRING

2022-11-08 08:35:53

架構微服務移動

2009-07-14 16:02:42

JDBC例子

2020-11-09 06:38:00

ninja構建方式構建系統

2009-08-19 04:14:00

線性鏈表

2023-02-07 10:40:30

gRPC系統Mac

2021-04-28 08:52:22

高并發架構設高并發系統

2023-01-03 12:30:25

架構CPUGPU

2025-01-22 08:00:00

架構秒殺系統Java

2013-03-26 14:17:21

架構架構設計事件驅動

2022-04-19 08:26:20

WebAPI架構

2011-09-08 13:41:53

Widget

2020-03-26 17:00:53

HashMapputJava

2010-04-19 17:21:36

Oracle寫文件
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩在线免费视频 | 国产精品完整版 | 91视视频在线观看入口直接观看 | 国产精品一区二区av | 久久久久99 | 你懂的免费在线 | 欧美精品1区2区 | 日韩爱爱网站 | 亚洲精品亚洲人成人网 | 青青青伊人 | 日韩成人免费 | 91国在线观看 | 亚洲一区导航 | 欧美无乱码久久久免费午夜一区 | 男女久久久 | 九九在线视频 | 大陆一级毛片免费视频观看 | 欧美日韩视频 | 日本一区二区三区在线观看 | 国产高清亚洲 | 日韩中文字幕视频在线 | 在线视频成人 | 91精品国产色综合久久 | 精品中文字幕在线观看 | 国产精品无码久久久久 | 国产精品亚洲综合 | 99视频在线播放 | 国产午夜精品久久久 | 激情久久网 | www.亚洲一区二区 | 日韩中文字幕在线播放 | 国产一区二区影院 | 美女黄网 | 欧美一区二区三区高清视频 | 欧美成年人 | 久久久精品网 | 91正在播放 | 午夜免费观看 | 91久久久www播放日本观看 | 夜夜久久 | 亚洲成人免费观看 |