iOS-MVVM--备用
我会修改一个关键架构,并将其带入我从那时起就在开发的各种应用,即使用一种叫做 Model-View-ViewModel 的架构替换 Model-View-Controller。
所以,MVVM 到底是什么?与其专注于说明 MVVM 的来历,不如让我们看一个典型的 iOS 是如何构建的,并从那里了解 MVVM:
我们看到的是一个典型的 MVC 设置。Model 呈现数据,View 呈现用户界面,而 View Controller 调节它两者之间的交互。Cool!
稍微考虑一下,虽然 View 和 View Controller 是技术上不同的组件,但它们几乎总是手牵手在一起,成对的。你什么时候看到一个 View 能够与不同 View Controller 配对?或者反过来?所以,为什么不正规化它们的连接呢?
这更准确地描述了你可能已经编写的 MVC 代码。但它并没有做太多事情来解决 iOS 应用中日益增长的重量级视图控制器的问题。在典型的 MVC 应用里,许多逻辑被放在 View Controller 里。它们中的一些确实属于 View Controller,但更多的是所谓的“表示逻辑(presentation logic)”,以 MVVM 属术语来说,就是那些将 Model 数据转换为 View 可以呈现的东西的事情,例如将一个 NSDate 转换为一个格式化过的 NSString。
我们的图解里缺少某些东西,那些使我们可以把所有表示逻辑放进去的东西。我们打算将其称为 “View Model” —— 它位于 View/Controller 与 Model 之间:
看起好多了!这个图解准确地描述了什么是 MVVM:一个 MVC 的增强版,我们正式连接了视图和控制器,并将表示逻辑从 Controller 移出放到一个新的对象里,即 View Model。MVVM 听起来很复杂,但它本质上就是一个精心优化的 MVC 架构,而 MVC 你早已熟悉。
现在我们知道了什么是 MVVM,但为什么我们会想要去使用它呢?在 iOS 上使用 MVVM 的动机,对我来说,无论如何,就是它能减少 View Controller 的复杂性并使得表示逻辑更易于测试。通过一些例子,我们将看到它如何达到这些目标。
此处有三个重点是我希望你看完本文能带走的:
MVVM 可以兼容你当下使用的 MVC 架构。
MVVM 增加你的应用的可测试性。
MVVM 配合一个绑定机制效果最好。
如我们之前所见,MVVM 基本上就是 MVC 的改进版,所以很容易就能看到它如何被整合到现有使用典型 MVC 架构的应用中。让我们看一个简单的 Person Model 以及相应的 View Controller:
看起好多了!这个图解准确地描述了什么是 MVVM:一个 MVC 的增强版,我们正式连接了视图和控制器,并将表示逻辑从 Controller 移出放到一个新的对象里,即 View Model。MVVM 听起来很复杂,但它本质上就是一个精心优化的 MVC 架构,而 MVC 你早已熟悉。
现在我们知道了什么是 MVVM,但为什么我们会想要去使用它呢?在 iOS 上使用 MVVM 的动机,对我来说,无论如何,就是它能减少 View Controller 的复杂性并使得表示逻辑更易于测试。通过一些例子,我们将看到它如何达到这些目标。
此处有三个重点是我希望你看完本文能带走的:
MVVM 可以兼容你当下使用的 MVC 架构。
MVVM 增加你的应用的可测试性。
MVVM 配合一个绑定机制效果最好。
如我们之前所见,MVVM 基本上就是 MVC 的改进版,所以很容易就能看到它如何被整合到现有使用典型 MVC 架构的应用中。让我们看一个简单的 Person Model 以及相应的 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
Cool!现在我们假设我们有一个 PersonViewController ,在 viewDidLoad 里,只需要基于它的 model 属性设置一些 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];
- }
这全都直截了当,标准的 MVC。现在来看看我们如何用一个 View Model 来增强它。
- @interface PersonViewModel : NSObject
- - (instancetype)initWithPerson:(Person *)person;
- @property (nonatomic, readonly) Person *person;
- @property (nonatomic, readonly) NSString *nameText;
- @property (nonatomic, readonly) NSString *birthdateText;
- @end
我们的 View Model 的实现大概如下:
- @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
Cool!我们已经将 viewDidLoad 中的表示逻辑放入我们的 View Model 里了。此时,我们新的 viewDidLoad 就会非常轻量:
- (void)viewDidLoad {
[super viewDidLoad];
self.nameLabel.text = self.viewModel.nameText;
self.birthdateLabel.text = self.viewModel.birthdateText;
}
所以,如你所见,并没有对我们的 MVC 架构做太多改变。还是同样的代码,只不过移动了位置。它与 MVC 兼容,带来更轻量的 View Controllers。
可测试,嗯?是怎样?好吧,View Controller 是出了名的难以测试,因为它们做了太多事情。在 MVVM 里,我们试着尽可能多的将代码移入 View Model 里。测试 View Controller 就变得容易多了,因为它们不再做一大堆事情,并且 View Model 也非常易于测试。让我们来看看:
- SpecBegin(Person)
- NSString *salutation = @"Dr.";
- NSString *firstName = @"first";
- NSString *lastName = @"last";
- NSDate *birthdate = [NSDate dateWithTimeIntervalSince1970:0];
- 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 中 Lable 的值。这样做不只是会变成一个麻烦的间接层,而且它只代表了一个十分脆弱的测试。现在,我们可以按意愿自由地修改视图层级而不必担心破坏我们的单元测试。使用 MVVM 带来的对于测试的好处非常清晰,甚至从这个简单的例子来看也可见一斑,而在有更复杂的表示逻辑的情况下,这个好处会更加明显。
注意到在这个简单的例子中, Model 是不可变的,所以我们可以只在初始化的时候指定我们 View Model 的属性。对于可变 Model,我们还需要使用一些绑定机制,这样 View Model 就能在背后的 Model 改变时更新自身的属性。此外,一旦 View Model 上的 Model 发生改变,那 View 的属性也需要更新。Model 的改变应该级联向下通过 View Model 进入 View。
在 OS X 上,我们可以使用 Cocoa 绑定,但在 iOS 上我们并没有这样好的配置可用。我们想到了 KVO(Key-Value Observation),而且它确实做了很伟大的工作。然而,对于一个简单的绑定都需要很大的样板代码,更不用说有许多属性需要绑定了。作为替代,我个人喜欢使用 ReactiveCocoa,但 MVVM 并未强制我们使用 ReactiveCocoa。MVVM 是一个伟大的典范,它自身独立,只是在有一个良好的绑定框架时做得更好。
我们覆盖了不少内容:从普通的 MVC 派生出 MVVM,看它们是如何相兼容的范式,从一个可测试的例子观察 MVVM,并看到 MVVM 在有一个配对的绑定机制时工作得更好。如果你有兴趣学习更多关于 MVVM 的知识,你可以看看这篇博客,它用更多细节解释了 MVVM 的好处,或者这一篇关于我们如何在最近的项目里使用 MVVM 获得巨大的成功的文章。我同样还有一个经过完整测试,基于 MVVM 的应用,叫做 C-41 ,它是开源的。
iOS-MVVM--备用的更多相关文章
- iOS - MVVM 架构模式
1.MVVM 从字面意思来理解,MVVM 即 Modal View ViewModel(模型 视图 视图模型).MVC 是一个用来组织代码的权威范式,也是构建 iOS App 的标准模式.Apple ...
- iOS——MVVM设计模式
一.典型的iOS构架——MVC 在典型的MVC设置中,Model呈现数据,Vie呈现用户界面,而ViewController调节它两者之间的交互. 虽然View和View Controller是技术上 ...
- [HMLY]7.iOS MVVM+RAC 从框架到实战
1.MVVM浅析 MVC是构建iOS App的标准模式,是苹果推荐的一个用来组织代码的权威范式,市面上大部分App都是这样构建的,具体组织模式不细说,iOS入门者都比较了解(虽然不一定能完全去遵守), ...
- iOS MVVM 参考
实践干货!猿题库 iOS 客户端架构设计 ReactiveCocoa入门教程 ReactiveCocoa入门教程——第二部 谈谈MVVM和MVC,使用swift集成RFP框架(ReactiveCoco ...
- iOS MVVM架构总结
为什么使用MVVM iOS中,我们使用的大部分都是MVC架构.虽然MVC的层次明确,但是由于功能日益的增加.代码的维护,使得更多的代码被写在了Controller中,这样Controller就显得非常 ...
- iOS MVVM+RAC 从基础到demo
一.关于经典模式MVC的简介 MVC是构建iOS App的标准模式,是苹果推荐的一个用来组织代码的权威范式,市面上大部分App都是这样构建的,具体组建模式不细说,iOS入门者都比较了解(虽然不一定能完 ...
- 开源 一行代码实现多形式多动画的推送小红点WZLBadge(iOS)-备用
更新日志 V1.2 2015.09.25 1.UITabBarItem badge is supproted; 2.Enable change badge properties when badge ...
- iOS MVVM 前世今生
MVVM,Model-View-ViewModel,一个从 MVC 模式中进化而来的设计模式,最早于2005年被微软的 WPF 和 Silverlight 的架构师 John Gossman 提出.
- ios MVVM实践 刷新网络请求+tableView展示数据
[实现效果] [目录结构相关] 此示例展示用的是MVVM结构形式,表述如下 M:数据Model的存储,可以用来对属性进行处理.(即胖model概念,上图中xx万人订阅这个处理方法写在Model内) V ...
- ios 设备用jquery live绑定 click 事件不管用
问题描述:用js拼接的html追加到页面,然后用 live 绑定click事件不起作用 解决办法:1.直接在标签写onclick事件 2.给需要绑定的标签添加css样式{cursor:pointe ...
随机推荐
- 【Android - V】之DrawerLayout的使用
DrawerLayout是Android V4包中的一个布局控件,用来实现一个抽屉样式的布局. DrawerLayout通过设置子视图的layout_gravity来决定子视图停靠在屏幕的哪个边缘外侧 ...
- C# 自动登录网页,浏览页面【转载】
需求:客户的数据同时存在在另外一个不可控的系统中,需要和当前系统同步. 思路:自动登录另外一个系统,然后抓取数据,同步到本系统中. 技术点:模拟用户登录:保存登录状态:抓取数据 /// <sum ...
- rsyslog 详解3
http://blog.clanzx.net/2013/12/31/rsyslog.html http://www.centosabc.com/archives/601 http://www.cnbl ...
- phpstorm 快捷方式 (备用)
常用快捷键 设置快捷键:File -> Settings -> IDE Settings -> Keymap -> 选择“eclipse” -> 然后“Copy”一份 ...
- Asp.net Mvc 第二回 UrlRouting
一.什么是UrlRouting 你可以使用UrlRouting来配置一些URL的映射,使用户可以按你的规则来访问网站. 使用UrlRouting,一定要规定URL模式,它包括一个位置标识,它将在你请求 ...
- 分享最近写的 两条sql语句
1. 搭建基本环境 插入测试数据 insert into jgdm (jgdm,jgmc) values('12300000000','河南省');insert into jgdm (jgdm,jg ...
- java基础之反射机制
一.概念 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为jav ...
- Vijos1734 NOI2010 海拔 平面图最小割
建立平面图的对偶图,把最小割转化成最短路问题 Dijkstra算法堆优化 (被输入顺序搞WA了好几次T_T) #include <cstdio> #include <cstring& ...
- c++primerplus(第六版)编程题——第3章(数据类型)
声明:作者为了调试方便,每一章的程序写在一个工程文件中,每一道编程练习题新建一个独立文件,在主函数中调用,我建议同我一样的初学者可以采用这种方式,调试起来会比较方便. 工程命名和文件命名可以命名成易识 ...
- 【POJ3468】【zkw线段树】A Simple Problem with Integers
Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. On ...