MVVM与Controller瘦身实践

前言

MVC是一个做iOS开发都知道的设计模式,也是Apple官方推荐的设计模式。实际上,Cocoa Touch就是按照MVC来设计的。

这里,我们先不讲MVC是什么,我们先来谈谈软件设计的一些原则或者说理念。在开发App的时候,我们的基本目标有以下几点:

  • 可靠性 - App的功能能够正常使用
  • 健壮性 - 在用户非正常使用的时候,app也能够正常反应,不要崩溃
  • 效率性 - 启动时间,耗电,流量,界面反应速度在用户容忍的范围以内

上文三点是表象层的东西,是大多数开发者或者团队会着重注意的。除了这三点,还有一些目标是工程方面的也是开发者要注意的:

  • 可修改性/可扩展性 - 软件需要迭代,功能不断完善
  • 容易理解 - 代码能够容易理解
  • 可测试性 - 代码能够方便的编写单元测试和集成测试
  • 可复用性 - 不用一次又一次造轮子

于是,软件设计领域有了几大通用设计原则来帮助我们实现这些目标:

单一功能原则,最少知识原则,聚合复用原则,接口隔离原则,依赖倒置原则,里氏代换原则,开-闭原则

这里的每一个原则都可以写单独的一篇文章,本文篇幅有限,不多讲解。

基于这些设计目标和理念,软件设计领域又有了设计模式。MVC/MVVM都是就是设计模式的一种。


MVC

历史

二十世纪世纪八十年代,Trygve Reenskaug在访问Palo Alto(施乐帕克)实验室的时候,第一次提出了MVC,并且在Smalltalk-76进行了实践,大名鼎鼎的施乐帕克实验室有很多划时代的研发成果:个人电脑,以太网,图形用户界面等。

在接下来的一段时间内,MVC不断的进化,基于MVC又提出了诸如MVP(model–view–presenter),MVVM(model–view–viewmodel)等设计模式。


组件

MVC设计模式按照职责将应用中的对象分成了三部分:Model,View,Controller。MVC除了将应用划分成了三个模块,还定义了模块之间的通信方式

Model

Model定义了你的应用是什么(What)。Model通常是纯粹的NSObject子类(Swift中可以是Struct/Class),仅仅用来表示数据模型。

Controller

Controller定义了Model如何显示给用户(How),并且View接收到的事件反馈到最后Model的变化。Controller层作为MVC的枢纽,往往要承担许多Model与View同步的工作。

View

View是Model的最终呈现,也就是用户看到的界面。


优点

MVC设计模式是是一个成熟的设计模式,也是Apple推荐的的设计模式,即使是刚入行的iOS开发者也多少了解这个设计模式,所以对于新人来说上手迅速,并且有大量的文档和范例来供我们参考。

在MVC模式中,View层是比较容易复用的,对应Cocoa中的UIView及其子类。所以,github的iOS开源项目中,View层也是最多的。

Model层涉及到了应用是什么,这一层非常独立,但是往往和具体业务相关,所以很难跨App服用。

既然只有Model-View-Controller三个组件,那么剩余的逻辑层代码就比较清楚了,全部堆积到Controller。


通信

MVC不仅定义了三类组件,还定义了组件之间通信的方式。

MVC三个组件之间的通信方式如图

Controller作为枢纽,它指向view和Model的线都是绿色的,意味着Controller可以直接访问(以引用的方式持有)Model和View。

View指向Controller的是虚线,虚线表示View到Controller的通信是盲通信的,原因也很简单:View是纯粹的展示部分,它不应该知道Controller是什么,它的工作就是拿到数据渲染出来。

那么,何为盲通信呢?简单来说当消息的发送者不知道接受者详细信息的时候,这样的通信就是盲通信。Cocoa Touch为我们提供了诸如delegate(dataSource)blocktarget/action这些盲通信方式。

Model指向Controller的同样也是虚线。原因也差不多,Model层代表的数据层应该与Controller无关。当Model改变的时候,通过KVO或者Notification的方式来通知Controller应当更新View。

这里有一点要提一下:UIViewController往往用来作为MVC中的Controller,MVC中的Controller也可以由其他类来实现


问题

通过上文的讲解,我们可以看到在纯粹的MVC设计模式中,Controller不得不承担大量的工作:

  • 网络API请求
  • 数据读写
  • 日志统计
  • 数据的处理(JSON<=>Object,数据计算)
  • 对View进行布局,动画
  • 处理Controller之间的跳转(push/modal/custom)
  • 处理View层传来的事件,返回到Model层
  • 监听Model层,反馈给View层

于是,大量的代码堆积在Controller层中,MVC最后成了Massive View Controller(重量级视图控制器)。

为了解决这种问题,我们通常会为Controller瘦身,也就是把Controller中代码抽出到不同的类中,引入MVVM就是为Controller瘦身的一个很好的实践。


MVVM

在MVVM设计模式中,组件变成了Model-View-ViewModel。

MVVM有两个规则

  • View持有ViewModel的引用,反之没有
  • ViewModel持有Model的引用,反之没有

图中,我们仍然以实线表示持有,虚线表示盲通信。

在iOS开发中,UIViewController是一个相当重要的角色,它是一个个界面的容器,负责接收各类系统的事件,能够实现界面专场的各种效果,配合NavigationController等能够轻易的实现各类界面切换。

在实践中,我们发现UIViewControllerView往往是绑定在一起的,比如UIViewController的一个属性就是view。在MVVM中,Controller可以当作一个重量级的View(负责界面切换和处理各类系统事件)。

不难看出,MVVM是对MVC的扩展,所以MVVM可以完美的兼容MVC。

对于一个界面来说,有时候View和ViewModel往往不止一个,MVVM也可以组合使用:


Controller解耦

MVC是一个优秀的设计模式,本文讲解MVVM也不是说想要用MVVM来替代MVC。对于软件设计来说,设计模式仅仅是一些参考工具,并没有固定的范式,使用起来是很灵活的。MVVM的很多理念对于Controller解耦是很有帮助的。

SubView

把相关的View放到一个Container View里,这样把对应View的创建,Layout等代码抽离出来,并且由Container统一处理用户交互,回调给外部。(这个比较好理解,就不举例子了)


TableView

关于TableView的Delegate/DataSource解耦,我单独写了一篇博客:

并且,提供了一个Swift开源库,来进行解耦:


Layout

在iOS中,视图的Layout一直是代码很乱的一块。通常Layout有两种

  • 手动的计算Frame - 简单粗暴,但是修改起来困难,易读性也不好
  • 通过约束AutoLayout - 有学习成本,并且不好debug,但是修改起来方便,也容易阅读。

通常使用Autolayout,我们都会用一些DSL的三方库:Masonry(OC),SnapKit(Swift)。

以一个常见的Layout为例,以下两图是在一个App中很常见的两种TableViewCell Layout:

两行列表

左边图,右边detail

这里,我们只关心左侧的图,在常规的Layout情况下Cell中的代码:

//Swift代码,使用SnapKit
leftImageView = UIIm
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值