如果你已经开发一段时间的iOS应用,你一定听说过Model-View-Controller,即MVC。MVC是构建iOS App的标准模式。然而,最近我已经越来越厌倦MVC的一些缺点。在本文,我将重温一下MVC是什么,详述它的缺点,并且告诉你一个新的方式来架构你的 App:Model-View-ViewModel。拿出你的流行语bingo card(宾果卡,一种游戏卡片-译者注),因为我们即将进行一次范式转变。
 
Model-View-Controller
Model-View-Controller是一个用来组织代码的权威范式。Apple甚至是这么说的。在MVC下,所有的对象被归类为一个model,一个view,或一个controller。Model持有数据,View显示与用户交互的界面,而View Controller调解Model和View之间的交互。
在上图中,view将用户交互通知给controller。view controller通过更新model来反应状态的改变。model(通常使用Key-Value-Observation)通知controller 来更新他们负责的view。大多数iOS应用程序的代码使用这种方式来组织。
 
模型model的对象通常非常非常的简单。很多时候,他们就是Core Data managed objects,或者避免使用Core Data,就是其他流行的数据模型层。根据Apple的文档,model包括数据和操作数据的业务逻辑。在实践中,model层往往非常薄,不管怎样,model层的业务逻辑被拖入了controller。
 
视图view通常是UIKit控件(component,这里根据习惯译为控件)或者编码定义的UIKit控件的集合。进入.xib或者 Storyboard会发现一个app、Button、Label都是由这些可视化的和可交互的控件组成。你懂的。View不应该直接引用model,并 且仅仅通过IBAction事件引用controller。业务逻辑很明显不归入view,视图本身没有任何业务。
 
还有控制器controller。Controller是app的“胶水代码”:协调模型和视图之间的所有交互。控制器负责管理他们所拥有的视 图的视图层次结构,还要响应视图的loading、appearing、disappearing等等,同时往往也会充满我们不愿暴露的model的模型 逻辑以及不愿暴露给视图的业务逻辑。这引出了第一个关于MVC的问题...
 
厚重的View Controller
由于大量的代码被放进view controller,导致他们变的相当臃肿。在iOS中有的view controller里绵延成千上万行代码的事并不是前所未见的。这些超重app的突出情况包括:厚重的View Controller很难维护(由于其庞大的规模);包含几十个属性,使他们的状态难以管理;遵循许多协议(protocol),导致协议的响应代码和 controller的逻辑代码混淆在一起。
 
厚重的view controller很难测试,不管是手动测试或是使用单元测试,因为有太多可能的状态。将代码分解成更小的多个模块通常是件好事。
 
遗失的网络逻辑
苹果使用的MVC的定义是这么说的:所有的对象都可以被归类为一个model,一个view,或是一个controller。就这些。那么把网络代码放哪里?和一个API通信的代码应该放在哪儿?
 
你可能试着把它放在model对象里,但是也会很棘手,因为网络调用应该使用异步,这样如果一个网络请求比持有它的model生命周期更长,事 情将变的复杂。显然也不应该把网络代码放在view里,因此只剩下controller了。这同样是个坏主意,因为这加剧了厚重View Controller的问题。
 
那么应该放在那里呢?显然MVC的3大组件根本没有适合放这些代码的地方。
 
较差的可测试性
MVC的另一个大问题是,它不鼓励开发人员编写单元测试。由于view controller混合了视图处理逻辑和业务逻辑,分离这些成分的单元测试成了一个艰巨的任务。大多数人选择忽略这个任务,那就是不做任何测试。
 
定义模糊的“Manage”
之前我提到了view controller可以管理试图的层次结构;view controller有一个“view”属性,并且可以通过IBOutlet访问视图的任何子视图。当有很多outlet时这样做不易于扩展,在某种意义 上,最好不要使用子视图控制器(child view controller)来帮助管理子视图(subview)。
 
要点在哪?验证用户输入的业务逻辑应归入controller还是model呢?
 
在这里有多个模糊的标准,似乎没有人能完全达成一致。貌似无论如何,view和对应的controller都紧紧的耦合在一起,总之,还是会把它们当成一个组件来对待。
 
Hey!现在有个点子...
 
Model-View-ViewModel
在理想的世界里,MVC也许工作的很好。然而,我们生活在真实的世界。既然我们已经详细说明了MVC在典型场景中的问题,那让我们看一看一个可供替换的选择:Model-View-ViewModel。
 
MVVM来自微软,不过不要坚持反对它。MVVM和MVC很像。它正式规范了视图和控制器紧耦合的性质,并引入新的组件。
在MVVM里,view和view controller正式联系在一起,我们把它们视为一个组件。视图view仍然不能直接引用模型model,当然controller也不能。相反,他们引用视图模型view model。
 
view model是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其他各种各样的代码的极好的地方。有一件事情不应归入view model,那就是任何视图本身的引用。view model的概念同时适用于于iOS和OS X。(换句话说,不要在view model中使用 #import UIKit.h)
 
由于展示逻辑(presentation logic)放在了view model中(比如model的值映射到一个格式化的字符串),视图控制器本身就会不再臃肿。当你开始使用MVVM的最好方式是,可以先将一小部分逻辑放 入视图模型,然后当你逐渐习惯于使用这个范式的时候再迁移更多的逻辑到视图模型中。
 
使用MVVM的iOS app是高度可测试的;因为view model包含了所有的展示逻辑并且不会引用view,所以它可以通过编程方式充分测试。虽然有众多的hack技术参与到测试Core Data模型,但使用MVVM写的app可以进行充分的单元测试。
 
以我的经验,使用MVVM会轻微的增加代码量,但总体上减少了代码的复杂性。这是一个划算的交易。
 
回过头再来看MVVM的图示,你会注意到我使用了模糊的动词“notify”和“update”,而没有详细说明该怎么做。你可以使用KVO,就像MVC那样,但这很快就会变得难以管理。事实上,使用ReactiveCocoa会是更好的方式来组织各个部分。
 
关于怎么结合ReactiveCocoa来使用MVVM的信息,可以阅读Colin Wheeler的excellent write-up或者看看我写的开源app。你也可以阅读我的关于ReactiveCocoa和MVVM的书

用Model-View-ViewModel构建iOS App的更多相关文章

  1. 用Model-View-ViewModel构建iOS App(转)

    转载自 Model-View-ViewModel for iOS [译] 如果你已经开发一段时间的iOS应用,你一定听说过Model-View-Controller, 即MVC.MVC是构建iOS a ...

  2. ios app开发步骤

    虽然开发一个app的任务看上去可能很艰巨,但是整个过程可以抽象成几个相对简单的步骤,下面这些步骤会在你开发第一个app时帮你步入正途. 定义Concept 每个好app都是从一个concept开始. ...

  3. 用Xamarin和Visual Studio编写iOS App

    一说开发 iOS app,你立马就会想到苹果的开发语言 Objective C/Swift 和 Xcode.但是,这并不是唯一的选择,我们完全可以使用别的语言和框架. 一种主流的替换方案是 Xamar ...

  4. 【IOS笔记】Using View Controllers in Your App

    参考:http://www.cnblogs.com/patientAndPersist/p/3279645.html Using View Controllers in Your App Whethe ...

  5. 关于报错:'sharedApplication' is unavailable: not available on iOS (App Extension) - Use view controller based

    最近在看Extension相关知识的时候,自己写了个小demo 发现[UIApplication sharedApplication]这个方法敲不出来了, 总是报错:'sharedApplicatio ...

  6. iOS9中找不到XXX.dylib 与 is unavailable no availabel on ios (app extension) - use view controller 的解决办法

    在 iOS9 中现在找不到 XXX.dylib 了,比如libz.tbd  如果要用到 libz.dylib,可以用下面的办法,来自 Stack Overflow. Go to Build Phase ...

  7. iOS App开发的那些事儿2:如何搭建合适的框架

    <iOS App开发的那些事儿>系列文章从更宏观的角度出发,不仅仅局限于具体某个功能.界面的实现,而是结合网易云信iOS端研发负责人多年的经验,从如何优化现有代码的角度出发,深度分析如何创 ...

  8. iOS App开发那些事:如何选择合适的人、规范和框架?

    http://www.cocoachina.com/ios/20141202/10386.html 自从做Team Leader之后,身上权责发生了变化,于是让我烦恼的不再是具体某个功能,某个界面的实 ...

  9. Qt Model/View(官方翻译,图文并茂)

    http://doc.trolltech.com/main-snapshot/model-view-programming.html 介绍 Qt 4推出了一组新的item view类,它们使用mode ...

随机推荐

  1. BZOJ 1901: Zju2112 Dynamic Rankings[带修改的主席树]【学习笔记】

    1901: Zju2112 Dynamic Rankings Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 7143  Solved: 2968[Su ...

  2. 台式机装原版Win2008R2

    台式机装原版Win2008R2 坑了老半天,总结出几点 1,系统os下载: http://msdn.itellyou.cn/ 注:其他地方下载的,装后发现不是起不来就是驱动装不了. 2,u盘里放个压缩 ...

  3. [LeetCode] Maximum Depth of Binary Tree 二叉树的最大深度

    Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the long ...

  4. [LeetCode] Search Insert Position 搜索插入位置

    Given a sorted array and a target value, return the index if the target is found. If not, return the ...

  5. tkinter事件机制

    一.tkinter.Event tkinter的事件机制跟js是一样的,也是只有一个Event类,这个类包罗万象,集成了键盘事件,鼠标事件,包含各种参数. 不像java swing那种强类型事件,sw ...

  6. 【Git】error: RPC

    摘要 git push throws error: RPC failed; result=56, HTTP code = 200的解决办法   原因 默认 Git 设置 http post 的缓存为 ...

  7. UEFI+GPT安装windows

    首先bios开启uefi windows安装选择驱动器界面:F10 进入命令行: list disk select disk 0 clean convert gpt

  8. Java并发_volatile实现可见性但不保证原子性

    读后感 介绍了volatile实现可见性的基本原理 介绍了volatile不能实现原子性的示例,volatile复合操作不能实现原子性,读取值后在自增前改值可能被其它线程读取并修改,自增后刷新值可能会 ...

  9. JS倒计时执行操作

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. 跟着《beginning jquery》学写slider插件并借助自定义事件改进它

    <beginning jquery>是一本很不错的学习jquery的书,作者的讲解深入浅出,很适合初学者,在最后一章里面,作者把前面所有的点结合起来完成了一个轮播图的jquery插件.实现 ...