一、典型的iOS构架——MVC

 

  在典型的MVC设置中,Model呈现数据,Vie呈现用户界面,而ViewController调节它两者之间的交互。

  虽然View和View Controller是技术上不同的组件,但他们总是手牵手在一起,成对的,View不能和不同的View Controller配对,反之亦然。View和View Controller之间的关系如下图所示:

  在典型的MVC应用里,许多逻辑被放在ViewController里。它们中的一些确实属于View Controller,但更多的是“表示逻辑”(presentation logic),就是那些将Model数据转换为View可以呈现的东西的事情,例如将一个NSData转换为一个格式化过的NSString。典型的MVC设计模式没有做太多的事情来解决iOS应用中日益增长的重量级视图控制器的问题。

二、MVC的增强版——MVVM  

  在MVC中增加一个View Model,把所有表示逻辑放进去,它位于Model和View Controller之间,如下图所示:

  在MVVM中,正式链接了视图(View)和控制器(View Controller),并将表示逻辑从ViewController移出放到一个新的对象里,即View Model。

  MVVM的优势:

    *能减少ViewController的复杂性,并使得表示逻辑更易于测试;

    *可以兼容MVC架构;

    *增加应用程序的可测试性;

    *配合一个绑定机制效果最好。

  MVVM基本上就是MVC的改进版,所以很容易整合到现有使用典型MVC架构的应用中。

  下面看一个简单的Person Model以及相应的View Controller:

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic,readonly) NSString *salutation;
@property (nonatomic,readonly) NSString *firstname;
@property (nonatomic,readonly) NSString *lastname;
@property (nonatomic,readonly) NSDate *birthDate; -(instancetype)initWithSalutation:(NSString *)salutation firstname:(NSString *)firstname lastname:(NSString *)lastname birthDate:(NSDate *)birthDate;
@end

下面有一个PersonViewController,在ViewDidLoad里,只需要基于他的model属性设置一些Label即可:

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

  

  这全都直接了当了,典型的MVC。现在来看看如何用一个ViewModel来增强它,创建一个PersonViewModel:

PersonViewModel.h

#import <Foundation/Foundation.h>
#import "Person.h" @interface PersonViewModel : NSObject
@property (nonatomic,readonly) Person *person;
@property (nonatomic,readonly) NSString *nameText;
@property (nonatomic,readonly) NSString *birthdateText;
-(instancetype)initWithPerson:(Person *)person;
@end

PersonViewModel.m

-(instancetype)initWithPerson:(Person *)person{
self = [super init];
if (!self) {
return nil;
}
_person = person;
if (person.salutation.length > ) {
_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;
}

  这样就将viewDidLoad中的表示逻辑加入viewModel里。此时,新的viewDidLoad就会非常轻量:

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

  因此,我们并没有对MVC架构做太多改变。还是同样的代码,只是移动了位置。它与MVC兼容,带来更轻量级的ViewControllers。

  在MVVM里,我们试着尽可能多的将代码移入View Model里。测试ViewController就变得容易多了,因为它们不再做一大堆事情,并且View Model也非常易于测试。现在看下面的代码:

SpecBegin(Person)
NSString *salutation = @"Dr.";
NSString *firstName = @"first";
NSString *lastName = @"last";
NSDate *birthdate = [NSDate dateWithTimeIntervalSince1970:]; it (@"should use the salutation available. ", ^{
Person *person = [[Person alloc] initWithSalutation:salutation firstName:firstName lastName:lastName birthdate:birthdate];
PersonViewModel *viewModel = [[PersonViewModel alloc] initWithPerson:person];
expect(viewModel.nameText).to.equal(@"Dr. first last");
}); it (@"should not use an unavailable salutation. ", ^{
Person *person = [[Person alloc] initWithSalutation:nil firstName:firstName lastName:lastName birthdate:birthdate];
PersonViewModel *viewModel = [[PersonViewModel alloc] initWithPerson:person];
expect(viewModel.nameText).to.equal(@"first last");
}); it (@"should use the correct date format. ", ^{
Person *person = [[Person alloc] initWithSalutation:nil firstName:firstName lastName:lastName birthdate:birthdate];
PersonViewModel *viewModel = [[PersonViewModel alloc] initWithPerson:person];
expect(viewModel.birthdateText).to.equal(@"Thursday January 1, 1970");
});
SpecEnd

  如果没有将这个逻辑移入View Model,我们将不得不实例化一个完整的View Controller以及伴随着的View,然后去比较View中Label值。这样做不只是会变成一个麻烦的间接层,而且它只代表了一个十分脆弱的测试。现在,可以按自己的意愿自由地修改视图层级而不必担心破坏单元测试。使用MVVM对测试带来的好处是显而易见的。

  注意到这个简单的例子中,Model是不可变的,所以可以只在初始化的时候指定View Model的属性。对于可变Model,还需要使用一些绑定机制,这样View Model就能在背后的Model改变时更新自身的属性。此为,一旦View Model上的Model发生改变,那View的属性也需要更新。Model的改变应该级联向下通过View Model进入View。

  在OS X上,可以使用cocoa绑定,但在iOS上并没有这样好的配置可用。但是,KVO为在iOS上实现提供了可能。

iOS——MVVM设计模式的更多相关文章

  1. 你真的了解iOS代理设计模式吗?

    在项目中我们经常会用到代理的设计模式,这是iOS中一种消息传递的方式,也可以通过这种方式来传递一些参数.这篇文章会涵盖代理的使用技巧和原理,以及代理的内存管理等方面的知识.我会通过这些方面的知识,带大 ...

  2. 【转】你真的了解iOS代理设计模式吗?

    转自:http://www.cocoachina.com/ios/20160317/15696.html 在项目中我们经常会用到代理的设计模式,这是iOS中一种消息传递的方式,也可以通过这种方式来传递 ...

  3. MVVM设计模式和WPF中的实现(四)事件绑定

    MVVM设计模式和在WPF中的实现(四) 事件绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...

  4. 浅谈 MVVM 设计模式在 Unity3D 中的设计与实施

    初识 MVVM 谈起 MVVM 设计模式,可能第一映像你会想到 WPF/Sliverlight,他们提供了的数据绑定(Data Binding),命令(Command)等功能,这让 MVVM 模式得到 ...

  5. iOS - MVVM 架构模式

    1.MVVM 从字面意思来理解,MVVM 即 Modal View ViewModel(模型 视图 视图模型).MVC 是一个用来组织代码的权威范式,也是构建 iOS App 的标准模式.Apple ...

  6. 设计模式笔记之三:Android DataBinding库(MVVM设计模式)

    本博客转自郭霖公众号:http://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&mid=2650236908&idx=1&sn=9e53 ...

  7. 包建强的培训课程(8):iOS与设计模式

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...

  8. 通过TodoList案例对比Vue.js的MVVM设计模式与JQuery的MVP设计模式

    Vue MVVM设计模式: 在使用vue进行编程时,不会再涉及到DOM的操作,取而代之的是修改数据层,当把数据进行变更的时候,vue之中它的底层会自动的根据数据的不同帮助我们去重新渲染页面. 编码时不 ...

  9. 使用MVVM设计模式构建WPF应用程序

    使用MVVM设计模式构建WPF应用程序 本文是翻译大牛Josh Smith的文章,WPF Apps With The Model-View-ViewModel Design Pattern,译者水平有 ...

随机推荐

  1. PHP之验证码类

    <?php /** * Created by PhpStorm. * User: Administrator * Date: 2016/6/20 * Time: 14:29 */ Class c ...

  2. poj1222 EXTENDED LIGHTS OUT

    设输入矩阵为A,输出矩阵为B,目标矩阵为C(零矩阵). 方便起见,矩阵行列下标均从1开始. 考虑A矩阵元素a(i,j),B矩阵中与其相邻的元素 b(i,j),b(i - 1, j),b(i + 1,j ...

  3. 2007 Asia - Nanjing F题,字典树

    题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=2 ...

  4. ZENG msgbox仿qq提示

    ZENG.msgbox.show("设置成功!", 4, 2000); ZENG.msgbox.show("服务器繁忙,请稍后再试.", 1, 2000); Z ...

  5. 内存恶鬼drawRect

    标题有点吓人,但是对于drawRect的评价倒是一点都不过分.在平日的开发中,随意覆盖drawRect方法,稍有不慎就会让你的程序内存暴增.下面我们来看一个例子. 去年的某天午后,北京的雾霾依旧像现在 ...

  6. COGS502. 长路上的灯

    502. 长路上的灯 ☆   输入文件:light.in   输出文件:light.out   简单对比时间限制:1 s   内存限制:128 MB [题目描述] 在一条无限长的路上,有一排无限长的路 ...

  7. for循环以及复杂if else 例题

    for循环语句 例一. 请输入一个整数,求1加到这个整数的和 例二 请从1到100 以内和7 有关的数字 例三,请设计一个游戏,前20关每关的分数是本身,21到30 是10分每关.31到40是20分每 ...

  8. C语言第3天标准的输入输出函数

    :first-child { margin-top: 0; } blockquote > :last-child { margin-bottom: 0; } img { border: 0; m ...

  9. Arduino中的setup()和loop()函数

    今天看arduino的源代码,对于arduino中的setup和loop有了新的理解,可能你以前对于这俩个函数就是知道arduino是初始化,而loop是死循环,但是托若你看了Arduino的主函数你 ...

  10. BZOJ 2627 JZPKIL

    题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2627 题意:计算下面式子 思路: A先不管.我们来搞B部分.下面说如何计算B这个最 ...