您好,登錄后才能下訂單哦!
本篇內容主要講解“iOS架構從MVC、MVP到MVVM源碼分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“iOS架構從MVC、MVP到MVVM源碼分析”吧!
MVC
設計模式M
: Model 數據層,負責網絡數據的處理,數據持久化存儲和讀取等工作V
: View 視圖層,負責呈現從數據層傳遞的數據渲染工作,以及與用戶的交互工作C
: Controller控制器,負責連接Model層跟View層,響應View的事件和作為View的代理,以及界面跳轉和生命周期的處理等任務
用戶點擊 View(視圖) --> 視圖響應事件 -->通過代理傳遞事件到Controller–>發起網絡請求更新Model—>Model處理完數據–>代理或通知給Controller–>改變視圖樣式–>完成
可以看到Controller強引用View與Model,而View與Model是分離的,所以就可以保證Model和View的可測試性和復用性,但是Controller不行,因為Controller是Model和View的中介,所以不能復用,或者說很難復用。
在我們實際開發中使用的MVC模式可以看到,View與Controller耦合在一起了。這是由于每一個界面的創建都需要一個Controller,而每一個Controller里面必然會帶一個View,這就導致了C和V的耦合。這種結構確實可以提高開發效率,但是一旦界面復雜就會造成Controller變得非常臃腫和難以維護。
我們要實現一個簡單的列表頁面,每行cell都一個按鈕,點擊按鈕前面數字1操作。
核心代碼:
// Controller - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ __weak typeof(self) wealSelf = self; MVCTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell_identifer"]; if(cell == nil){ cell = [[MVCTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell_identifer"]; } DemoModel *model = self.dataArray[indexPath.row]; [cell loadDataWithModel:model]; cell.clickBtn = ^{ NSLog(@"id===%ld",model.num); [wealSelf changeNumWithModel:model]; }; cell.selectionStyle = UITableViewCellSelectionStyleNone; return cell; } /* * 用戶點擊事件通過Block傳遞過來后,在Controller層處理更新Mdoel以及更新視圖的邏輯 */ - (void)changeNumWithModel:(DemoModel*)model{ model.num++; NSIndexPath *path = [NSIndexPath indexPathForRow:model.Id inSection:0]; [self.mainTabelView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationLeft]; }
可以看到用戶點擊事件通過Block傳遞過來后,在Controller層處理更新Mdoel以及更新視圖的邏輯
MVP
設計模式M
: Model 數據層,負責網絡數據的處理,數據持久化存儲和讀取等工作V
: View 視圖層,負責呈現從數據層傳遞的數據渲染工作,以及與用戶的交互,這里把Controller層也合并到視圖層P
: Presenter層,負責視圖需要數據的獲取,獲取到數據后刷新視圖。響應View的事件和作為View的代理。
可以看到 MVP模式跟原始的MVC模式非常相似,完全實現了View與Model層的分離,而且把業務邏輯放在了Presenter層中,視圖需要的所有數據都從Presenter獲取,而View與 Presenter通過協議進行事件的傳遞。
用戶點擊 View(視圖) --> 視圖響應事件 -->通過代理傳遞事件到Presenter–>發起網絡請求更新Model–>Model處理完數據–>代理或通知給視圖(View或是Controller)–>改變視圖樣式–>完成
//DemoProtocal import <Foundation/Foundation.h> @protocol DemoProtocal <NSObject> @optional //用戶點擊按鈕 觸發事件: UI改變傳值到model數據改變 UI --- > Model 點擊cell 按鈕 -(void)didClickCellAddBtnWithIndexPathRow:(NSInteger)index; //model數據改變傳值到UI界面刷新 Model --- > UI -(void)reloadUI; @end
我們把所有的代理抽象出來,成為一個Protocal文件。這兩個方法的作用:
-(void)didClickCellAddBtnWithIndexPathRow:(NSInteger)index;
:Cell視圖調用它去Presenter層實現點擊邏輯的處理
-(void)reloadUI;
: Presenter調用它去更新主視圖View或者Controller
//Presenter.h #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import "DemoProtocal.h" NS_ASSUME_NONNULL_BEGIN @interface Presenter : NSObject @property (nonatomic, strong,readonly) NSMutableArray *dataArray; @property (nonatomic, weak) id<DemoProtocal>delegate;//協議,去更新主視圖UI // 更新 TableView UI 根據需求 - (void)requestDataAndUpdateUI; //更新 cell UI - (void)updateCell:(UITableViewCell*)cell withIndex:(NSInteger)index; @end
dataArray
: 視圖需要的數據源
- (void)requestDataAndUpdateUI;
:主視圖Controller調用,去更新自己的UI
- (void)updateCell:(UITableViewCell*)cell withIndex:(NSInteger)index;
:更新 Cell的UI
//Controller 層 - (void)iniData{ self.presenter = [[Presenter alloc] init]; self.presenter.delegate = self; [self.presenter requestDataAndUpdateUI]; } ... - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return self.presenter.dataArray.count; } - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ MVPTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell_identifer"]; if(cell == nil){ cell = [[MVPTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell_identifer"]; } //更新cell UI 數據 [self.presenter updateCell:cell withIndex:indexPath.row]; cell.selectionStyle = UITableViewCellSelectionStyleNone; return cell; } #pragma mark - DemoProtocal //Presenter 的代理回調 數據更新了通知View去更新視圖 - (void)reloadUI{ [self.mainTabelView reloadData]; }
Controller層初始化Presenter,調用其方法更新自己的UI,可以看到網絡數據的獲取,處理都在Presenter中,處理完成后通過協議回調給Controller去reload數據
//Cell - (void)addBtnDown:(UIButton*)btn{ NSLog(@"%s",__func__); if([self.delegate respondsToSelector:@selector(didClickCellAddBtnWithIndexPathRow:)]){ [self.delegate didClickCellAddBtnWithIndexPathRow:self.index]; } }
Cell層點擊事件通過協議調用,而這個協議方法的實現是在Presenter中實現的。
MVP模式也有自身的缺點,所有的用戶操作和更新UI的回調需要定義,隨著交互越來越復雜,這些定義都要有很大一坨代碼。邏輯過于復雜的情況下,Present本身也會變得臃腫。所以衍生出了MVVM模式。
MVVM+RAC
設計模式M
: Model 數據層,負責網絡數據的處理,數據持久化存儲和讀取等工作V
: View 視圖層,此時的視圖層包括Controller,負責呈現從數據層傳遞的數據渲染工作,以及與用戶的交互VM
:ViewModel層,負責視圖需要數據的獲取,獲取到數據后刷新視圖。響應View的事件和作為View的代理等工作。
通過架構圖可以看到,MVVM模式跟MVP模式基本類似。主要區別是在MVP基礎上加入了雙向綁定機制。當被綁定對象某個值的變化時,綁定對象會自動感知,無需被綁定對象主動通知綁定對象。可以使用KVO和RAC實現。我們這里采用了RAC的實現方式。
我們這里包括兩層視圖:主視圖Controller以及Cell,分別對應兩層ViewModel:ViewModel和CellViewModel
//ViewModel.h @interface ViewModel : NSObject //發送數據請求的Rac,可以去訂閱獲取 請求結果 @property (nonatomic,strong,readonly) RACCommand *requestCommand; @property (nonatomic,strong) NSArray *dataArr;//返回子級對象的ViewModel - (CellViewModel *)itemViewModelForIndex:(NSInteger)index; @end
RACCommand *requestCommand
:提供供主視圖調用的命令,調用它去獲取網絡數據
NSArray *dataArr
: 提供供主視圖使用的數據源,注意這里不能用NSMutableArray,因為NSMutableArray不支持KVO,不能被RACObserve。
- (CellViewModel *)itemViewModelForIndex:(NSInteger)index;
根據Cell的index返回它需要的的ViewModel
@interface CellViewModel : NSObject @property (nonatomic,copy,readonly) NSString *titleStr; @property (nonatomic,copy,readonly) NSString *numStr; @property (nonatomic,copy,readonly) RACCommand *addCommand; - (instancetype)initWithModel:(DemoModel *)model; @end
CellViewModel
: 暴露出Cell渲染需要的所有數據
RACCommand *addCommand;
: 按鈕點擊事件的指令,觸發后需要在CellViewModel里面做處理。
//controller - (void)iniData{ self.viewModel = [[ViewModel alloc] init]; // 發送請求 RACSignal *signal = [self.viewModel.requestCommand execute:@{@"page":@"1"}]; [signal subscribeNext:^(id x) { NSLog(@"x=======%@",x); if([x boolValue] == 1){//請求成功 [self.mainTabelView reloadData]; } }]; } - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ MVVMTableVIewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell_identifer"]; if(cell == nil){ cell = [[MVVMTableVIewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell_identifer"]; } //更新cell UI 數據 cell.cellViewModel = [self.viewModel itemViewModelForIndex:indexPath.row]; cell.selectionStyle = UITableViewCellSelectionStyleNone; return cell; }
iniData
:初始化ViewModel,并發送請求命令。這里可以監聽這個完成信號,進行刷新視圖操作
cell.cellViewModel = [self.viewModel itemViewModelForIndex:indexPath.row];
根據主視圖的ViewModel去獲取Cell的ViewModel,實現cell的數據綁定。
//TableViewCell RAC(self.titleLabel,text) = RACObserve(self, cellViewModel.titleStr); RAC(self.numLabel,text) = RACObserve(self, cellViewModel.numStr); [[self.addBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) { NSLog(@">>>>>"); [self.cellViewModel.addCommand execute:nil]; }];
在Cell里面進行與ViewModel的數據綁定,這邊有個注意Racobserve左邊只有self右邊才有viewModel.titleStr這樣就避Cell重用的問題。
[self.cellViewModel.addCommand execute:nil];
:按鈕的點擊方法觸發,事件的處理在CellViewModel中。
到此,相信大家對“iOS架構從MVC、MVP到MVVM源碼分析”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。