我于 2011 年在 500px 找到本身的第一份 iOS 开发工做。虽然我已经在大学里作了好几年 iOS 外包开发,但这才是个人一个真正的 iOS 开发工做。我被做为惟一的 iOS 开发者被招聘去实现拥有漂亮设计的 iPad 应用。在短短七周里,咱们就发布了 1.0 并持续迭代,添加了更多特性,但从本质上,代码库也变得更加复杂了。编程
有时我感受就像我不知道在作什么。虽然我知道本身的设计模式——就像任何好的编程人员那样 —— 但我太接近我在作的产品以致于不能客观地衡量个人架构决策的有效性。当队伍中来了另一位开发者时,我意识到咱们陷入困境了。设计模式
从没听过 MVC ?有人称之为 Massive View Controller(重量级视图控制器),这就是咱们那时候的感受。我不打算介绍使人汗颜的细节,但说实在的,若是我不得再也不次重来一次,我绝对会作出不一样的决策。架构
我会修改一个关键架构,并将其带入我从那时起就在开发的各类应用,即便用一种叫作 Model-View-ViewModel 的架构替换 Model-View-Controller。框架
因此,MVVM 究竟是什么?与其专一于说明 MVVM 的来历,不如让咱们看一个典型的 iOS 是如何构建的,并从那里了解 MVVM:
咱们看到的是一个典型的 MVC 设置。Model 呈现数据,View 呈现用户界面,而 View Controller 调节它二者之间的交互。Cool!mvvm
稍微考虑一下,虽然 View 和 View Controller 是技术上不一样的组件,但它们几乎老是手牵手在一块儿,成对的。你何时看到一个 View 可以与不一样 View Controller 配对?或者反过来?因此,为何不正规化它们的链接呢?
这更准确地描述了你可能已经编写的 MVC 代码。但它并无作太多事情来解决 iOS 应用中日益增加的重量级视图控制器的问题。在典型的 MVC 应用里,许多逻辑被放在 View Controller 里。它们中的一些确实属于 View Controller,但更多的是所谓的“表示逻辑(presentation logic)”,以 MVVM 属术语来讲,就是那些将 Model 数据转换为 View 能够呈现的东西的事情,例如将一个 NSDate 转换为一个格式化过的 NSString。post
咱们的图解里缺乏某些东西,那些使咱们能够把全部表示逻辑放进去的东西。咱们打算将其称为 “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:优化
1 |
@interface Person : NSObject |
Cool!如今咱们假设咱们有一个 PersonViewController ,在 viewDidLoad 里,只须要基于它的 model 属性设置一些 Label 便可。
1 |
- (void)viewDidLoad { |
这全都直截了当,标准的 MVC。如今来看看咱们如何用一个 View Model 来加强它。
1 |
@interface PersonViewModel : NSObject |
咱们的 View Model 的实现大概以下:
1 |
@implementation PersonViewModel |
Cool!咱们已经将 viewDidLoad 中的表示逻辑放入咱们的 View Model 里了。此时,咱们新的 viewDidLoad 就会很是轻量:
1 |
- (void)viewDidLoad { |
因此,如你所见,并无对咱们的 MVC 架构作太多改变。仍是一样的代码,只不过移动了位置。它与 MVC 兼容,带来更轻量的 View Controllers。
可测试,嗯?是怎样?好吧,View Controller 是出了名的难以测试,由于它们作了太多事情。在 MVVM 里,咱们试着尽量多的将代码移入 View Model 里。测试 View Controller 就变得容易多了,由于它们再也不作一大堆事情,而且 View Model 也很是易于测试。让咱们来看看:
1 |
SpecBegin(Person) |
若是咱们没有将这个逻辑移入 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 ,它是开源的。