細說IOS工程架構(持續更新)
一、框架模式的選擇
1. MVC框架
MVC全名是Model View Controller,是模型(model)-檢視(view)-控制器(controller)的縮寫,一種軟體設計典範,用一種業務邏輯、資料、介面顯示分離的方法組織程式碼,將業務邏輯聚集到一個部件裡面,在改進和個性化定製介面及使用者互動的同時,不需要重新編寫業務邏輯。MVC被獨特的發展起來用於對映傳統的輸入、處理和輸出功能在一個邏輯的圖形化使用者介面的結構中。
1.1 MVC 程式設計模式
MVC 是一種使用 MVC(Model View Controller 模型-檢視-控制器)設計應用程式的模式
-
檢視(View):負責資料展示、監聽使用者觸控等工作
-
控制器(Controller):負責業務邏輯、事件響應、資料加工等工作
-
模型(Controller):負責封裝資料、儲存和處理資料運算等工作
1.2 MVC通訊特點
1. Model和View永遠不能相互通訊,只能通過Controller傳遞。
2. Controller可以直接與Model通訊(讀寫呼叫Model),Model通過Notification和KVO機制與Controller間接通訊。
3. Controller與View通過Target/Action,delegate和datasource三種模式進行通訊。通過這三種模式,View就可以向Controller通訊,Action/Target 模式來讓Controller 監聽View 觸發的事件。View又通過Data source和delegate進行資料獲取和某些通訊操作。
1.3 MVC優缺點
1.易用性:與其他幾種模式相比最小的程式碼量,維護起來也較為容易。
2. 可測性:由於糟糕的分散性,只能對Model進行測試。
3. 均衡性:厚重的ViewController、無處安放的網路邏輯與資料邏輯。
2. MVCS
蘋果自身就採用的是這種架構思路,從名字也能看出,也是基於MVC衍生出來的一套架構。從概念上來說,它拆分的部分是Model部分,拆出來一個Store。這個Store專門負責資料存取。
從實際操作的角度上講,它拆開的是Controller。因為Controller做了資料儲存的事情,就會變得非常龐大,那麼就把Controller專門負責存取資料的那部分抽離出來,交給另一個物件去做,這個物件就是Store。這麼調整之後,整個結構也就變成了真正意義上的MVCS。
-
檢視(View):使用者介面
-
控制器(Controller):業務邏輯及處理
-
模型(Model):資料儲存
-
儲存器(Store):資料處理邏輯
MVCS是基於瘦Model的一種架構思路,把原本Model要做的很多事情中的其中一部分關於資料儲存的程式碼抽象成了Store,在一定程度上降低了Controller的壓力。
3. MVP模式
MVP(Model-View-Presenter)是從經典的模式MVC演變而來,它們的基本思想有相通的地方Controller/Presenter負責邏輯的處理,Model提供資料,View負責顯示。
3.1 MVP模式的優缺點
1. 模型與檢視完全分離,我們可以修改檢視而不影響模型。
2. 可以更高效地使用模型,因為所有的互動都發生在Presenter內部。
3. 可以將一個Presenter用於多個檢視,而不需要改變Presenter的邏輯。
4. 如果我們把邏輯放在Presenter中,那麼我們就可以脫離使用者介面來測試這些邏輯。
5. 由於對檢視的渲染放在了Presenter中,檢視和Presenter的互動會過於頻繁,一旦檢視需要變更,Presenter也需要變更了。
3.2 MVP與MVC區別:
1. 在MVP中View並不直接使用Model,它們之間的通訊是通過Presenter (MVC中的Controller)來進行的,所有的互動都發生在Presenter內部,而在MVC中View會直接從Model中讀取資料而不是通過 Controller。
2. 在MVC裡,View是可以直接訪問Model的。View裡會包含Model資訊,不可避免的還要包括一些業務邏輯。 在MVC模型裡,Model不依賴於View,但是View是依賴於Model的。因為有一些業務邏輯在View裡實現了,導致View的可重用性降低。
3. 在MVC裡,不建議在 View 中依賴 Model,而是儘可能把業務邏輯都放在 Controller 中處理,使View 只和 Controller 互動。
3.MVVM框架
MVVM是Model-View-ViewModel的簡寫。它本質上就是MVC 的改進版。MVVM 就是將其中的View 的狀態和行為抽象化,讓我們將檢視 UI 和業務邏輯分開。當然這些事 ViewModel 已經幫我們做了,它可以取出 Model 的資料同時幫忙處理 View 中由於需要展示內容而涉及的業務邏輯。
MVVM(Model-View-ViewModel)框架的由來便是MVP(Model-View-Presenter)模式與WPF結合的應用方式時發展演變過來的一種新型架構框架。它立足於原有MVP框架並且把WPF的新特性糅合進去,以應對客戶日益複雜的需求變化。
3.1 MVVM模式的組成部分
-
模型:模型是指代表真實狀態內容的領域模型(物件導向),或指代表內容的資料訪問層(以資料為中心)。
-
檢視:就像在MVC和MVP模式中一樣,檢視是使用者在螢幕上看到的結構、佈局和外觀(UI)。
-
檢視模型:檢視模型是暴露公共屬性和命令的檢視的抽象。在檢視模型中,繫結器在檢視和資料繫結器之間進行通訊。
3.2 MVVM優點
-
低耦合:View可以獨立於Model變化和修改,一個ViewModel可以繫結到不同的View上,當View變化的時候Model可以不變,當Model變化的時候View也可以不變。
-
可重用性:你可以把一些檢視邏輯放在一個ViewModel裡面,讓很多view重用這段檢視邏輯。
-
獨立開發:開發人員可以專注於業務邏輯和資料的開發(ViewModel),設計人員可以專注於頁面設計。
-
可測試:介面素來是比較難於測試的,而現在測試可以針對ViewModel來寫。
3.2 MVVM與MVP區別:
mvvm模式將Presener改名為View Model,基本上與MVP模式完全一致,唯一的區別是,MVVM可以結合RAC(MVVM+RAC)實現view與ViewModel的雙向繫結,這樣開發者就不用處理接收事件和View更新的工作。
二、基礎類庫與cocoaPod應用
1. 基礎類庫的目錄分佈
BaseKit-專案的基礎程式碼目錄,存放著通用的基礎類庫,應用於各個業務模組。
ServiceMgr:服務公共類庫,與Kernal相比偏業務性,如資料上報、行為驗證、使用者資訊、共享資料等。
Kernal:基礎公共類庫,主要提供基礎能力,如Network、Log、dataBase等。
Utils:工具類集合(類方法),如Utils、JPUtils、Authoritys、Encrypt等 。
Categorys:公共類別擴充套件,可按照類屬性的不同分為不同的子目錄。
Views:通用檢視元件,如ZoomScrollView、BannerView、AlertView等。
Base:基礎類,又分為MVC三個子目錄,封裝存放Basic類。
2. cocoaPod應用
cocoaPod應用IOS開發者應該都比較熟悉,主要是用關聯第三方庫與私有元件庫,方便版本迭代管理。
2.1 GitHub第三方庫
CocoaPods詳解之-使用篇:https://blog.csdn.net/meegomeego/article/details/24005567
2.2 GitLab私有庫
CocoaPod-spec私有庫配置:https://blog.csdn.net/z119901214/article/details/90241251
三、業務元件&元件通訊
1. 業務元件
業務元件主要是指作為一個大的業務模組,單獨分離成一個元件的形式,如電商模組、聊天模組、部落格等。業務元件之間不存在耦合程式碼,由元件通訊中間層實現彼此的通訊。
1.1 組價分層方式
-
目錄分層:在主業務目錄下,按照不同的業務元件建立不同的目錄,業務模組獨立,元件之間不直接呼叫API。
-
多project分層:通過xcworkspace的方式,不同的業務模組建立各自的project工程,業務project工程run成功後,將framework引入主工程中。
1.2 元件分層方式的特點
-
目錄分層:目錄分層比較簡單,沒有實現真正的程式碼分割,所以目錄與目錄直接的類是可以引用的,這樣就很依賴於團隊開發的規範性。
-
多project分層:多project分層實現了不同業務元件程式碼的分割,作為framework的形式引入,靜態framework的形式引入更新迭代成本比較高,動態framework的形式引入又會影響到編譯打包的效率,而且不利於cocoaPod的使用。
2. 元件之間的通訊
2.1 UR-Block(MGJRouter)
/*********** 業務元件中註冊 ***********/
[[MKRouter sharedInstance] registerHandler:^(MKRouteRequest *request) {
// 跳轉至商祥頁
// request.callBack(nil, @{@"productId" : request[@"productId"]});
} forRoute:MKString(@".*product/detail.*\\?(.*)\\&(.*)$")];
/*********** 模組呼叫 ***********/
[[MKRouter sharedInstance] handleURL:[NSURL URLWithString:@"weixin://com.apple.iphone/product/detail?productId=ID985632"] params:nil targetCallBack:^(NSError *error, NSDictionary *responseObject) {
}];
資料埋點: "appData/click?params={eventId:0925D6A034A58F06}"
原生商詳跳轉: "product/detail?productId=id1314785643"
weex商詳跳轉: "weex/page?params='jsonString的URL編碼'"
web商詳跳轉: "web/page?params='jsonString的URL編碼'"
flutter商詳跳轉: "flutter/page?params='jsonString的URL編碼'"
2.2 Target-Action(CTMediator)
/*********** 公共實現 ***********/
#import "MKMediator+Feature.h"
static NSString * const kTargeFeature = @"Feature";
static NSString * const kActionViewControllerForFeature = @"viewControllerForFeature";
@implementation MKMediator (Feature)
- (UIViewController *)mediator_ViewControllerForFeature:(MKFeatureActionModel *)params {
return [self call:kTargeFeature action:kActionViewControllerForFeature parameters:params];
}
@end
/*********** 業務元件中實現 ***********/
#import "MKMediator+Feature.h"
#import "MKTarget_Feature.h"
#import "AudioVideoPraticeVC.h"
@implementation MKTarget_Feature
- (UIViewController *)action_viewControllerForFeature:(MKActionModel *)params {
AudioVideoPraticeVC* audioVC = [[AudioVideoPraticeVC alloc] init];
return audioVC;
}
@end
/*********** 模組呼叫 ***********/
MKActionModel* acitonModel = [[MKActionModel alloc] init];
actionModel.keyValues = @{};
actionModel.complete = ^(id result) {
};
UIViewController* audioVC = [[MKMediator shared] mediator_ViewControllerForFeature:acitonModel];
原生->原生錄音頁:直接呼叫對應業務元件的公共API
外部->原生錄音頁: "native/page?target=Feature&action=viewControllerForFeature¶ms='jsonString的URL編碼'"(直接解析url呼叫)
外部->原生錄音頁: "native/page/audio/index.html?key=value&key1=value1"(通過url正則匹配到頁面的相關配置,最終得到target、action、params)
開啟hybrid容器頁:通過url正則匹配到頁面的相關配置,再呼叫業務容器元件的公共API實現頁面的跳轉和渲染
2.3 Protocol-Class
面向介面程式設計,通過protocol定義協議介面與屬性(注意:protocol的屬性只是一個宣告,並沒有實際用途,需要實現協議的類本身定義了該屬性)。
/* 定義協議方法與屬性 */
@protocol MKOpenURLProtocol <NSObject>
@property (nonatomic, assign) BOOL isPresent;
- (BOOL)openURLWithURLString:(NSString *)URLString params:(NSDictionary *)params;
@end
MKOpenURLExecutor宣告瞭MKOpenURLProtocol,並實現其協議方法。
/* 執行者-MKOpenURLExecutor.h */
@interface MKOpenURLExecutor : NSObject <MKOpenURLProtocol>
@property (nonatomic, assign) BOOL isPresent;
@end
/* 實現者-MKOpenURLExecutor.m */
- (BOOL)openURLWithURLString:(NSString *)URLString params:(NSDictionary *)params {
// do something
return YES;
}
@end
上面講的這種宣告協議再到類的實現,雖然是應用了介面程式設計的方式,但是業務元件之間還是會有直接呼叫的關係,所以需要有個中間者實現protocol與介面物件的一一匹配。
/* 中間者-MKProtocolManager.h */
@interface MKProtocolManager : NSObject
+ (instancetype)sharedInstance;
- (void)setObject:(id)object protocol:(Protocol *)protocol;
- (id)objectWithProtocol:(Protocol *)protocol;
@end
/* 中間者-MKProtocolManager.m */
@interface MKProtocolManager ()
@property (nonatomic, strong) NSMutableDictionary* protocolSet;
@end
@implementation MKProtocolManager
+ (instancetype)sharedInstance {
static MKProtocolManager* instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
instance.protocolSet = [NSMutableDictionary dictionary];
});
return instance;
}
- (void)setObject:(id)object protocol:(Protocol *)protocol {
if (object && protocol) {
[_protocolSet setObject:object forKey:NSStringFromProtocol(protocol)];
}
}
- (id)objectWithProtocol:(Protocol *)protocol {
if (protocol) {
return [_protocolSet objectForKey:NSStringFromProtocol(protocol)];
}
return nil;
}
@end
業務元件A的協議與介面物件的繫結,以及其他業務元件中呼叫業務元件A的協議介面。
/* 業務元件A的協議與介面物件的一一匹配 */
+ (void)registerProtocol {
MKOpenURLExecutor* executor = [[MKOpenURLExecutor alloc] init];
executor.isPresent = NO; // 內部屬性配置
[[MKProtocolManager sharedInstance] setObject:executor protocol:@protocol(MKOpenURLProtocol)];
}
/* 其他業務元件中呼叫業務元件A的協議介面 */
- (void)openURL {
id <MKOpenURLProtocol> protocol = [[MKProtocolManager sharedInstance] objectWithProtocol:@protocol(MKOpenURLProtocol)];
[protocol openURLWithURLString:@"" params:@{}];
}
2.4 三種通訊方式的特點
-
URL-Block:能解決元件間的依賴,路由配置靈活,需要去註冊&維護路由表,方法呼叫不夠直觀。
-
Target-Action:統一了元件api服務,元件與框架之間無依賴關係,需要額外維護中介軟體類擴充套件,方法呼叫不夠直觀。
-
Protocol-Class:面向介面程式設計,方法呼叫比較直觀,每個元件都需要宣告一個對外的協議,和一個用於實現的類。
3. 頁面路由規則配置(推送、scheme、JSAPI-openURL)
type(型別) | key(頁面key) | url(頁面url) | regular(正則) | arguments(頁面配置) |
---|---|---|---|---|
native | home_parge | url | regular | arguments |
web | order_detail | url | regular | arguments |
weex | star_info | url | regular | lx_url、arguments |
react native | order_list | url | regular | rn_url、arguments |
flutter | mine_detail | url | regular | routeName、arguments |
通過url正則匹配到相關的路由配置,生成對應型別的VC容器,從而實現頁面跳轉。(url連結必須實現URL編解碼)
native:通過頁面配置資訊與url引數,生成對應的原生頁面,最終實現頁面的跳轉與渲染。
web:直接通過url,生成WebVC,實現頁面跳轉和H5資源載入。
weex:獲取weex資原始檔的下載連結及頁面配置引數,傳遞給weex容器實現資源載入和頁面渲染。
react naive:獲取rn資原始檔的下載連結及頁面配置引數,傳遞給rn容器實現資源載入和頁面渲染。
flutter:配置相關的routeName及頁面配置引數,傳遞給Flutter容器實現flutter端頁面的渲染。
相關文章
- JiaoZiVideoPlayer使用說明(持續更新中...)IDE
- SAP Commerce(原Hybris)的一些架構圖,持續更新架構
- Jenkins+iOS持續整合細節記錄JenkinsiOS
- 簡單聊聊,如何構建測試工程師的能力模型 (持續更新)工程師模型
- 前端工程師面試必備(持續更新中)前端工程師面試
- iOS開發常用小技巧記錄(持續更新)iOS
- iOS開發備忘筆記 (持續更新中)iOS筆記
- JVM(持續更新。。。)JVM
- FastApi持續更新ASTAPI
- 聊聊持續交付與軟體架構架構
- Xcode 技巧 持續更新XCode
- Blender 雕刻 持續更新
- 寫給前端工程師的Linux實戰教程【持續更新】前端工程師Linux
- 高階前端工程師面試必備(持續更新中)前端工程師面試
- React SSR重構踩坑記錄(持續更新)React
- 一個前端工程師的Docker學習筆記【持續更新】前端工程師Docker筆記
- Pycharm快捷鍵持續更新PyCharm
- MySql報錯(持續更新)MySql
- leetcode題解【持續更新】LeetCode
- LevOJ平臺 - 持續更新
- AnimalController 學習 持續更新Controller
- idea快捷鍵(持續更新)Idea
- git使用、持續更新中Git
- LINUX進階(持續更新)Linux
- 細說JUC的執行緒池架構執行緒架構
- 細說HTTP增量更新HTTP
- iOS持續整合(一)——fastlane 使用iOSAST
- Flutter 問題集,持續更新Flutter
- 收集的乾貨,持續更新
- 前端知識點(持續更新)前端
- SDN名言摘錄(持續更新)
- 開發常識 持續更新~~
- 陣列總結,持續更新~陣列
- 軟考筆記 --- 持續更新筆記
- Python Redis常用操作(持續更新)PythonRedis
- go 常用包整理 (持續更新)Go
- AI面試題(持續更新)AI面試題
- 數學小結(持續更新)