欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

iOS架構(gòu)從?MVC、MVP?到?MVVM

 更新時(shí)間:2023年03月24日 15:23:23   作者:九州編程  
這篇文章主要介紹了iOS架構(gòu)從MVC、MVP到MVVM,文章關(guān)于MVC設(shè)計(jì)模式以及架構(gòu)等均介紹的非常詳細(xì),需要的朋友可以作為參考

概述

做了這么多年的客戶端研發(fā)一直在使用蘋果爸爸推薦的MVC架構(gòu)模式。MVC從應(yīng)用層面進(jìn)行分層開發(fā),極大優(yōu)化了我們的代碼結(jié)構(gòu),簡單易上手,很容易被程序員所接受。程序員剛接手一個(gè)新項(xiàng)目,如果是MVC的架構(gòu)模式,會(huì)減少代碼熟悉時(shí)間,快速的進(jìn)行開發(fā)和維護(hù)工作,實(shí)際上對于多人開發(fā)維護(hù)的項(xiàng)目,MVC仍然是非常好的架構(gòu)模式,這也是這種架構(gòu)模式經(jīng)久不衰的原因。
但是任何事物都有兩面性,隨著項(xiàng)目需求的增加,業(yè)務(wù)邏輯、網(wǎng)絡(luò)請求、代理方法等都往Controller層加塞,導(dǎo)致Controller層變得越來越臃腫,動(dòng)輒上千行的代碼量絕對是維護(hù)人員的噩夢,因此在MVC基礎(chǔ)上逐漸衍生出來了MVP、MVVM等架構(gòu)模式。
本文是基于OC代碼進(jìn)行闡述的,使用iOS開發(fā)經(jīng)典的 TableView 列表來分析每個(gè)架構(gòu)模式。相信看了這篇文章你會(huì)有所領(lǐng)悟。當(dāng)然一千個(gè)人眼中有一千種哈姆雷特,具體在業(yè)務(wù)開發(fā)中使用哪種模式需要你自己去衡量。

1.傳統(tǒng)的MVC設(shè)計(jì)模式

MVC
M: Model 數(shù)據(jù)層,負(fù)責(zé)網(wǎng)絡(luò)數(shù)據(jù)的處理,數(shù)據(jù)持久化存儲(chǔ)和讀取等工作
V: View 視圖層,負(fù)責(zé)呈現(xiàn)從數(shù)據(jù)層傳遞的數(shù)據(jù)渲染工作,以及與用戶的交互工作
C: Controller控制器,負(fù)責(zé)連接Model層跟View層,響應(yīng)View的事件和作為View的代理,以及界面跳轉(zhuǎn)和生命周期的處理等任務(wù)

用戶的交互邏輯

用戶點(diǎn)擊 View(視圖) --> 視圖響應(yīng)事件 -->通過代理傳遞事件到Controller–>發(fā)起網(wǎng)絡(luò)請求更新Model—>Model處理完數(shù)據(jù)–>代理或通知給Controller–>改變視圖樣式–>完成

可以看到Controller強(qiáng)引用View與Model,而View與Model是分離的,所以就可以保證Model和View的可測試性和復(fù)用性,但是Controller不行,因?yàn)镃ontroller是Model和View的中介,所以不能復(fù)用,或者說很難復(fù)用。

iOS開發(fā)實(shí)際使用的MVC架構(gòu)

在實(shí)際MVC在我們實(shí)際開發(fā)中使用的MVC模式可以看到,View與Controller耦合在一起了。這是由于每一個(gè)界面的創(chuàng)建都需要一個(gè)Controller,而每一個(gè)Controller里面必然會(huì)帶一個(gè)View,這就導(dǎo)致了C和V的耦合。這種結(jié)構(gòu)確實(shí)可以提高開發(fā)效率,但是一旦界面復(fù)雜就會(huì)造成Controller變得非常臃腫和難以維護(hù)。

MVC代碼示例
我們要實(shí)現(xiàn)一個(gè)簡單的列表頁面,每行cell都一個(gè)按鈕,點(diǎn)擊按鈕前面數(shù)字?1操作

mvcexamp核心代碼:

// 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;
}
/*
* 用戶點(diǎn)擊事件通過Block傳遞過來后,在Controller層處理更新Mdoel以及更新視圖的邏輯
*/
- (void)changeNumWithModel:(DemoModel*)model{
    
    model.num++;
    NSIndexPath *path = [NSIndexPath indexPathForRow:model.Id inSection:0];
    [self.mainTabelView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationLeft];
}

可以看到用戶點(diǎn)擊事件通過Block傳遞過來后,在Controller層處理更新Mdoel以及更新視圖的邏輯

2.MVP設(shè)計(jì)模式

MVPM: Model 數(shù)據(jù)層,負(fù)責(zé)網(wǎng)絡(luò)數(shù)據(jù)的處理,數(shù)據(jù)持久化存儲(chǔ)和讀取等工作
V: View 視圖層,負(fù)責(zé)呈現(xiàn)從數(shù)據(jù)層傳遞的數(shù)據(jù)渲染工作,以及與用戶的交互,這里把Controller層也合并到視圖層
P: Presenter層,負(fù)責(zé)視圖需要數(shù)據(jù)的獲取,獲取到數(shù)據(jù)后刷新視圖。響應(yīng)View的事件和作為View的代理。

可以看到 MVP模式跟原始的MVC模式非常相似,完全實(shí)現(xiàn)了View與Model層的分離,而且把業(yè)務(wù)邏輯放在了Presenter層中,視圖需要的所有數(shù)據(jù)都從Presenter獲取,而View與 Presenter通過協(xié)議進(jìn)行事件的傳遞。

用戶的交互邏輯

用戶點(diǎn)擊 View(視圖) --> 視圖響應(yīng)事件 -->通過代理傳遞事件到Presenter–>發(fā)起網(wǎng)絡(luò)請求更新Model–>Model處理完數(shù)據(jù)–>代理或通知給視圖(View或是Controller)–>改變視圖樣式–>完成

MVP代碼示例

項(xiàng)目結(jié)構(gòu)

//DemoProtocal
import <Foundation/Foundation.h>


@protocol DemoProtocal <NSObject>
@optional
//用戶點(diǎn)擊按鈕 觸發(fā)事件: UI改變傳值到model數(shù)據(jù)改變  UI --- > Model 點(diǎn)擊cell 按鈕
-(void)didClickCellAddBtnWithIndexPathRow:(NSInteger)index;
//model數(shù)據(jù)改變傳值到UI界面刷新 Model --- > UI
-(void)reloadUI;
@end

我們把所有的代理抽象出來,成為一個(gè)Protocal文件。這兩個(gè)方法的作用:
-(void)didClickCellAddBtnWithIndexPathRow:(NSInteger)index;:Cell視圖調(diào)用它去Presenter層實(shí)現(xiàn)點(diǎn)擊邏輯的處理
-(void)reloadUI;: Presenter調(diào)用它去更新主視圖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;//協(xié)議,去更新主視圖UI
// 更新 TableView UI 根據(jù)需求
- (void)requestDataAndUpdateUI;
//更新 cell UI
- (void)updateCell:(UITableViewCell*)cell withIndex:(NSInteger)index;
@end

dataArray : 視圖需要的數(shù)據(jù)源
- (void)requestDataAndUpdateUI;:主視圖Controller調(diào)用,去更新自己的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 數(shù)據(jù)
? ? [self.presenter updateCell:cell withIndex:indexPath.row];
? ? cell.selectionStyle = UITableViewCellSelectionStyleNone;
? ? return cell;
}

#pragma mark - DemoProtocal
//Presenter 的代理回調(diào) 數(shù)據(jù)更新了通知View去更新視圖
- (void)reloadUI{
? ? [self.mainTabelView reloadData];
}

Controller層初始化Presenter,調(diào)用其方法更新自己的UI,可以看到網(wǎng)絡(luò)數(shù)據(jù)的獲取,處理都在Presenter中,處理完成后通過協(xié)議回調(diào)給Controller去reload數(shù)據(jù)

//Cell
- (void)addBtnDown:(UIButton*)btn{
? ? NSLog(@"%s",__func__);
? ? if([self.delegate respondsToSelector:@selector(didClickCellAddBtnWithIndexPathRow:)]){
? ? ? ? [self.delegate didClickCellAddBtnWithIndexPathRow:self.index];
? ? }
}

Cell層點(diǎn)擊事件通過協(xié)議調(diào)用,而這個(gè)協(xié)議方法的實(shí)現(xiàn)是在Presenter中實(shí)現(xiàn)的。

MVP模式也有自身的缺點(diǎn),所有的用戶操作和更新UI的回調(diào)需要定義,隨著交互越來越復(fù)雜,這些定義都要有很大一坨代碼。邏輯過于復(fù)雜的情況下,Present本身也會(huì)變得臃腫。所以衍生出了MVVM模式。

3.MVVM+RAC設(shè)計(jì)模式

MVVM
M: Model 數(shù)據(jù)層,負(fù)責(zé)網(wǎng)絡(luò)數(shù)據(jù)的處理,數(shù)據(jù)持久化存儲(chǔ)和讀取等工作
V: View 視圖層,此時(shí)的視圖層包括Controller,負(fù)責(zé)呈現(xiàn)從數(shù)據(jù)層傳遞的數(shù)據(jù)渲染工作,以及與用戶的交互
VM:ViewModel層,負(fù)責(zé)視圖需要數(shù)據(jù)的獲取,獲取到數(shù)據(jù)后刷新視圖。響應(yīng)View的事件和作為View的代理等工作。
通過架構(gòu)圖可以看到,MVVM模式跟MVP模式基本類似。主要區(qū)別是在MVP基礎(chǔ)上加入了雙向綁定機(jī)制。當(dāng)被綁定對象某個(gè)值的變化時(shí),綁定對象會(huì)自動(dòng)感知,無需被綁定對象主動(dòng)通知綁定對象。可以使用KVO和RAC實(shí)現(xiàn)。我們這里采用了RAC的實(shí)現(xiàn)方式。關(guān)于RAC如果不熟悉的小伙伴可以點(diǎn)這里,我們這篇文章不在涉及。

MVVM代碼示例

我們這里包括兩層視圖:主視圖Controller以及Cell,分別對應(yīng)兩層ViewModel:ViewModel和CellViewModel

//ViewModel.h

@interface ViewModel : NSObject
//發(fā)送數(shù)據(jù)請求的Rac,可以去訂閱獲取 請求結(jié)果
@property (nonatomic,strong,readonly) RACCommand *requestCommand;
@property (nonatomic,strong) NSArray *dataArr;//返回子級對象的ViewModel
- (CellViewModel *)itemViewModelForIndex:(NSInteger)index;
@end

RACCommand *requestCommand:提供供主視圖調(diào)用的命令,調(diào)用它去獲取網(wǎng)絡(luò)數(shù)據(jù)
NSArray *dataArr: 提供供主視圖使用的數(shù)據(jù)源,注意這里不能用NSMutableArray,因?yàn)镹SMutableArray不支持KVO,不能被RACObserve。
- (CellViewModel *)itemViewModelForIndex:(NSInteger)index; 根據(jù)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渲染需要的所有數(shù)據(jù)
RACCommand *addCommand;: 按鈕點(diǎn)擊事件的指令,觸發(fā)后需要在CellViewModel里面做處理。

//controller
- (void)iniData{
? ? self.viewModel = [[ViewModel alloc] init];
? ? // 發(fā)送請求
? ? 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 數(shù)據(jù)
? ? cell.cellViewModel = [self.viewModel itemViewModelForIndex:indexPath.row];
? ? cell.selectionStyle = UITableViewCellSelectionStyleNone;
? ? ? ??
? ? return cell;
}

iniData:初始化ViewModel,并發(fā)送請求命令。這里可以監(jiān)聽這個(gè)完成信號,進(jìn)行刷新視圖操作
cell.cellViewModel = [self.viewModel itemViewModelForIndex:indexPath.row]; 根據(jù)主視圖的ViewModel去獲取Cell的ViewModel,實(shí)現(xiàn)cell的數(shù)據(jù)綁定。

//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里面進(jìn)行與ViewModel的數(shù)據(jù)綁定,這邊有個(gè)注意Racobserve左邊只有self右邊才有viewModel.titleStr這樣就避Cell重用的問題。
[self.cellViewModel.addCommand execute:nil];:按鈕的點(diǎn)擊方法觸發(fā),事件的處理在CellViewModel中。

總結(jié)

到此這篇關(guān)于iOS架構(gòu)從 MVC、MVP 到 MVVM的文章就介紹到這了,更多相關(guān)淺談MVC的內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!。

相關(guān)文章

最新評論