MVVM架構(gòu)

概要

MVC架構(gòu),Model-View-Controller,如圖一所示為一個(gè)典型的MVC設(shè)置。


圖一:mvc
  • Model呈現(xiàn)數(shù)據(jù)
  • View呈現(xiàn)用戶界面
  • Controller調(diào)節(jié)兩者之間的交互。從Model取數(shù)據(jù),顯示在View中。

典型的MVC應(yīng)用里,許多邏輯被放在View Controller中,他們中一些確實(shí)屬于View Controller,但更多的是表現(xiàn)邏輯,即將Model中數(shù)據(jù)轉(zhuǎn)換為View可以呈現(xiàn)的內(nèi)容的事情。例如將JSON包里的某個(gè)NSDate轉(zhuǎn)換為特定格式的NSString。這也導(dǎo)致了MVC被人稱作Massive-View-Controller(重量級(jí)視圖控制器)。

通常Controller中應(yīng)該只放如下代碼:

  • 初始化時(shí)構(gòu)造相應(yīng)的View和Model
  • 監(jiān)聽Model層的事件,將Model層的數(shù)據(jù)傳遞到View層
  • 監(jiān)聽View層的事件,將View層的事件傳遞到Model層

僅此而已,除此之外的任何邏輯都不應(yīng)該放到Controller中。因此這也就有了MVVM

MVVM

圖二所示為MVVM設(shè)置:MVVM其實(shí)就是MVC的增強(qiáng)版。我們正式連接了View 和View Controller,并將表示邏輯從Controller中移出,放到了一個(gè)新的對(duì)象里,即View Model中。


圖二:MVVM.png

這樣做可帶來(lái)如下的益處:

  • 減少View Controller的復(fù)雜性,使得表示邏輯易于測(cè)試。
  • 兼容MVC模式
  • MVVM 配合一個(gè)綁定機(jī)制效果最好

舉例

下面是一個(gè)看一個(gè)簡(jiǎn)單的 Person Model 以及相應(yīng)的 View Controller。

@interface Person : NSObject

- (instancetype)initwithSalutation:(NSString *)salutation firstName:(NSString *)firstName lastName:(NSString *)lastName birthdate:(NSDate *)birthdate;

@property (nonatomic, readonly) NSString *salutation;
@property (nonatomic, readonly) NSString *firstName;
@property (nonatomic, readonly) NSString *lastName;
@property (nonatomic, readonly) NSDate *birthdate;

@end

現(xiàn)在假設(shè)有一個(gè) PersonViewController,在 viewDidLoad里,只需要基于它的 model屬性設(shè)置一些Label即可。

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    if (self.model.salutation.length > 0) { 
        self.nameLabel.text = [NSString stringWithFormat:@"%@ %@ %@",self.model.salutation,self.model.firstName, self.model.lastName];
    } else { 
        self.nameLabel.text = [NSString stringWithFormat:@"%@ %@", self.model.firstName,self.model.lastName];
    } 
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 
    [dateFormatter setDateFormat:@"EEEE MMMM d, yyyy"];
    self.birthdateLabel.text = [dateFormatter stringFromDate:model.birthdate];
}

現(xiàn)在來(lái)看怎樣通過(guò)一個(gè)ViewModel來(lái)增強(qiáng)它。

//ViewModel.h
@interface PersonViewModel : NSObject

- (instancetype)initWithPerson:(Person *)person;

@property (nonatomic, readonly) Person *person;
@property (nonatomic, readonly) NSString *nameText;
@property (nonatomic, readonly) NSString *birthdateText;

@end

ViewModel實(shí)現(xiàn)如下

//ViewModel.m
@implementation PersonViewModel

- (instancetype)initWithPerson:(Person *)person { 
    self = [super init]; 
    if (!self) return nil; 
    _person = person; 
    if (person.salutation.length > 0) { 
    _nameText = [NSString stringWithFormat:@"%@ %@ %@", self.person.salutation, self.person.firstName, self.person.lastName]; 
    } else { 
        _nameText = [NSString stringWithFormat:@"%@ %@", self.person.firstName, self.person.lastName];
    }
     NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"EEEE MMMM d, yyyy"]; 
    _birthdateText = [dateFormatter stringFromDate:person.birthdate]; 
    return self;
}

@end

現(xiàn)在我們已將viewDidLoad中的表示邏輯放入我們的 View Model 里了。此時(shí),我們新的 viewDidLoad
就會(huì)非常輕量:

- (void)viewDidLoad { [super viewDidLoad]; 
    self.nameLabel.text = self.viewModel.nameText; 
    self.birthdateLabel.text = self.viewModel.birthdateText;
}

MVVM作用與問(wèn)題

MVVM在實(shí)際使用時(shí)也有一定的問(wèn)題,主要體現(xiàn)在兩點(diǎn):

  • 數(shù)據(jù)綁定使得 Bug 很難被調(diào)試。你看到界面異常了,有可能是你 View 的代碼有 Bug,也可能是 Model 的代碼有問(wèn)題。數(shù)據(jù)綁定使得一個(gè)位置的 Bug 被快速傳遞到別的位置,要定位原始出問(wèn)題的地方就變得不那么容易了。
  • 對(duì)于過(guò)大的項(xiàng)目,數(shù)據(jù)綁定需要花費(fèi)更多的內(nèi)存。

ReactiveCocoa

MVVM引出了一個(gè)ReactiveCocoa,ReactiveCocoa作用如圖三所示:


圖三:MVVM ReactiveCocoa.png

如果想要深入了解ReactiveCocoa,可以看下ReactiveCocoa這篇文章。

備注:本文為讀書筆記,主要參考了如下幾篇文章:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容