如果您开发iOS应用已有一段时间,您一定听说过模型-视图-控制器或MVC。MVC是构建iOS应用程序的标准模式。然而,最近我对MVC的一些缺点越来越厌烦了。在本文中,我将回顾什么是MVC,详细说明它的缺点,并向您介绍一种构建应用程序的新方法:模型-视图-视图模型。拿出你的流行语宾果卡,因为我们即将经历范式转变。模型-视图-控制器模型-视图-控制器是组织代码的权威范例。苹果甚至是这么说的。在MVC下,所有对象都被分类为模型、视图或控制器。模型保存数据,视图显示与用户交互的界面,视图控制器调解模型和视图之间的交互。在上图中,视图将用户交互通知给控制器。视图控制器通过更新模型来响应状态变化。模型(通常使用Key-Value-Observation)通知控制器更新他们负责的视图。大多数iOS应用程序的代码都是这样组织的。模型对象通常非常非常简单。很多时候,它们是CoreData托管对象,或者避免使用CoreData,是其他流行的数据模型层。根据Apple的文档,模型包括数据和操作数据的业务逻辑。在实践中,model层往往很薄,无论如何把model层的业务逻辑拖到controller中。View视图通常是一个UIKit控件(组件,这里按照习惯翻译成控件)或者通过编码定义的UIKit控件的集合。进入.xib或者Storyboard会发现一个app,Button,Label都是由这些可视化交互控件组成的。你知道。View不应该直接引用模型,只能通过IBAction事件引用控制器。业务逻辑显然不包含在视图中,视图本身没有业务。还有控制器控制器。控制器是应用程序的“胶水代码”:它协调模型和视图之间的所有交互。控制器负责管理自己拥有的视图的视图层次结构,响应视图的加载、出现、消失等。同时,它们往往充满了我们不想暴露给视图的模型逻辑和我们不想暴露给视图的业务逻辑。.这就引出了关于MVC的第一个问题……厚视图控制器由于放入视图控制器的代码量而变得非常臃肿。在iOS的某些视图控制器中扩展数千行代码并非闻所未闻。这些超重应用程序的突出案例包括:难以维护的重型视图控制器(由于其庞大的尺寸);包含数十个属性,使得它们的状态难以管理;符合很多协议(protocol),导致协议响应代码和控制器逻辑代码混合在一起。厚视图控制器很难测试,无论是手动还是使用单元测试,因为有太多可能的状态。将代码分解成更小的多个模块通常是一件好事。缺少网络逻辑Apple使用的MVC定义是这样的:所有对象都可以归类为模型、视图或控制器。就是这样。那么网络代码放在哪里呢?与API通信的代码应该放在哪里?您可以尝试将它放在模型对象中,但这会很棘手,因为网络调用应该异步进行,因此如果网络调用比持有它的模型寿命更长,事情就会变得复杂。显然,网络代码不应该放在视图中,所以只剩下控制器了。这也是一个坏主意,因为它加剧了厚视图控制器的问题。那么应该放在哪里呢?很显然,MVC的三大组件根本就没有地方放这些代码。可测试性差MVC的另一个大问题是它不鼓励开发人员编写单元测试。由于视图控制器混合了视图处理逻辑和业务逻辑,因此分离这些组件的单元测试成为一项艰巨的任务。大多数人选择忽略这个任务,即不做任何测试。在定义模棱两可的“管理”之前,我提到视图控制器可以管理视图层次结构;视图控制器有一个“视图”属性,可以通过IBOutlet访问视图的任何子视图。当有很多出口时,这样做不容易扩展,从某种意义上说,最好不要使用子视图控制器来帮助管理子视图。重点在哪里?验证用户输入的业务逻辑应该属于控制器还是模型?这里有多种模糊的标准,似乎没有人完全同意。看来无论如何,view和对应的controller是紧耦合在一起的,总之还是会被当作一个组件来对待。嘿!现在有一个想法……模型-视图-视图模型在理想的世界中,MVC可能工作得很好。然而,我们生活在现实世界中。说完MVC在典型场景下的问题,我们再来看一个替代方案:Model-View-ViewModel。MVVM来自Microsoft,但不要反对它。MVVM与MVC非常相似。它形式化了视图和控制器的紧密耦合性质,并引入了新组件。在MVVM中,视图和视图控制器在形式上是联系在一起的,我们将它们视为一个组件。view视图还是不能直接引用model模型,controller当然不能。相反,它们指的是视图模型视图模型。视图模型是放置用户输入验证逻辑、视图显示逻辑、进行网络请求和其他杂项代码的绝佳场所。不应归属于视图模型的一件事是对视图本身的任何引用。视图模型的概念适用于iOS和OSX。(换句话说,不要在视图模型中使用#importUIKit.h。)由于表示逻辑放在视图模型中(例如值模型映射到格式化字符串),视图控制器本身将不再臃肿。开始使用MVVM的最佳方法是先将少量逻辑放入视图模型,然后在您习惯使用此范例后将更多逻辑迁移到视图模型。使用MVVM的iOS应用程序是高度可测试的;因为视图模型包含所有表示逻辑并且不引用视图,所以可以通过编程方式对其进行全面测试。虽然在测试CoreData模型时涉及到大量hack,但使用MVVM编写的应用程序可以进行充分的单元测试。根据我的经验,使用MVVM会稍微增加代码大小,但总体上会降低代码复杂性。这是一个可靠的交易。回头看看MVVM图,您会注意到我使用了模糊的动词“通知”和“更新”,而没有详细说明要做什么。您可以像MVC一样使用KVO,但这很快就会变得难以管理。事实上,使用ReactiveCocoa将是一种更好的组织部件的方式。有关如何将MVVM与ReactiveCocoa结合使用的信息,请阅读ColinWheeler的精彩文章或查看我编写的开源应用程序。您还可以阅读我关于ReactiveCocoa和MVVM的书。
