先说MVC吧
MVC,全称是 Model View Controller
,是模型 (model
)-视图 (view
)-控制器 (controller
) 的缩写。它表示的是一种常见的客户端软件开发框架;
MVC
这个概念最早出现在上个世纪八十年代(目测本文的读者都还没有出生,当然笔者也没有出生)被提出,之后被广泛的应用于各类软件开发中.
iOS
开发也不例外,iOS
的系统架构就是典型的MVC
,并且为我们实现了V
层和C
层的基础,然后开发者根据相应的数据补充M
.这一切看起都很合理,而且开发者也一直都这么做着,并没有什么不妥
嗯,是的,没毛病...
但是事实真的如此么?
事实是,很多维护者会抱怨前人的代码多烂多烂,真的没法看,设计模式一塌糊涂等等;
这时候的MVC
可能会变成(Massive View Controller
),controller
里面塞满了各类业务代码,多大上千行,甚至更多,如果注释写的好一些,勉强能看,如果没有注释...
为什么会这样?这里的原因太多了,比如有些开发者在做iOS
之前并没有听说过MVC
,有些即使知道MVC
,也根本无法明确MVC
到底干嘛的?面试官经常会问到这个问题,而得到的答案也仅仅是解释MVC
概念之类,很少有人会在解释完概念之后再去仔细的阐述一下结构设计,代码规范之类...
下面我们仔细剖析一下,到底该如何MVC
?
真正的MVC
我们来看看 MVC
这种架构的特点。其实设计模式很多时候是为了 Don't repeat yourself
原则来做的,该原则要求能够复用的代码要尽量复用,来保证重用。在 MVC
这种设计模式中,我们发现 View
和 Model
都是符合这种原则的。
对于View
来说,你如果抽象得好,那么一个App
的动画效果可以很方便地移植到别的App
上,而Github
上也有很多UI
控件,这些控件都是在View
层做了很好的封装设计,使得它能够方便地开源给大家复用。
对于Model
来说,它其实是用来存储业务的数据的,如果做得好,它也可以方便地复用。比如我当时在做有道云笔记 iPad
版的时候,我们就直接和iOS
版复用了所有的Model
层的代码。在创业做猿题库客户端时,iOS
和iPad
版的 Model
层代码再次被复用上了。当然,因为和业务本身的数据意义相关,Model
层的复用大多数是在一个产品内部,不太可能像 View
层那样开源给社区。
说完 View
和 Model
了,那我们想想 Controller
,Controller
有多少可以复用的?我们写完了一个 Controller
之后,可以很方便地复用它吗?结论是:非常难复用。在某些场景下,我们可能可以用addSubViewController
之类的方式复用 Controller
,但它的复用场景还是非常非常少的。
如果我们能够意识到 Controller
里面的代码不便于复用,我们就能知道什么代码应该写在 Controller
里面了,那就是那些不能复用的代码。在我看来,Controller
里面就只应该存放这些不能复用的代码,这些代码包括:
- 在初始化时,构造相应的 View 和 Model。
- 监听 Model 层的事件,将 Model 层的数据传递到 View 层。
- 监听 View 层的事件,并且将 View 层的事件转发到 Model 层。
MVC反面教材
直接上代码吧:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableviewcell", for: indexPath) as! TableViewCell
cell.textLabel?.text = "二狗子"
cell.imageView?.image = UIImage(named: "")
return cell
}
上面这段代码,咋一看也没啥,因为只有6行而已,但是这是因为上面的cell
数据加载很简单,只有一个text
和image
,但实际开发中,我们的数据可不是这样的,如果是新浪微博那种cell
我想,单单是cell
的数据加载都能写个上百行;所以这种写法是完全违反MVC
原则的,因为你在C
里面干了V
的事情, C
只应该做数据传递;所以,严格遵循MVC
原则的写法是,不管cell
的数据加载多么负责,代码都应该是下面这样的:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableviewcell", for: indexPath) as! TableViewCell
cell.userModel = dataModel[indexPath.row]
return cell
}
如果你的tableview
是个标准的反面教材,请在评论区留下你的大名📌
这里只是简单的做了一些代码结构的调整,但是确实符合了MVC
原则,但是在controller
中往往不止这些东西,controller
最头疼的问题,就是网络请求问题,因为复杂的界面会涉及到很多数据请求,而这些请求也会伴随着界面和数据的变化,这些逻辑写上去,那么controller
的代码量瞬间就上去了,几个版本迭代过去,你懂的!所以,达到上千行只是时间问题?
何解?
在网上关于这个问题的答案有很多,比如分离网络业务层,直接放到一个专门的业务模块里面,MVVM
就是在这种情况下衍生出来的.
MVVM
MVVM
的历史,这里就不赘述了,感兴趣的同学直接百度,一大把,先看看一张基本上只要谈到MVVM
都会放的一张图:
上面这张图,我想很多人肯定不止一次看到过
这个图准确的描述了什么是MVVM
:一个MVC
的增强版,让view
和viewcontroller
直接结合在一起,使用ViewModel
进行链接,并将逻辑从controller
里面移出放到ViewModel
;MVVM
听起来很复杂,但是他本质还是MVC
,只不过在结构上做了调整;这样view
和model
彻底解耦了,那么这个时候他们之间的通信会变得有些奇怪了,因为他们中间跨越了一个层,所以在MVVM
的使用当中,通常会利用双向绑定技术,使得Model
的变化能同步更新到View
上
那么这里就得提到一个框架:ReactiveCocoa
(swift
和Objective - C
都支持,但是在Swift
上更推荐使用RxSwift
);
ReactiveCocoa
是一个基于Cocoa
封装的函数响应式框架,使用它可以轻松的实现数据的绑定;所以,每每谈到MVVM
的时候,总会提到它;但ReactiveCocoa
可以不想其他三方框架那般,看看Document
就可以开心的撸起来了,ReactiveCocoa
的Document
都能写成一本厚厚的书了,而且最关键的是,它的编程思想,函数式,响应式,数据流...
还有它那魔性的语法.
当然了ReactiveCocoa
也提供了很多便捷:
- 函数式,提供了很多函数式操作(对于
Objective - C
来说,Swift
本身自带) - 数据绑定,很多时候可以代替
KVO
,delegate
- 动态声明
更多信息可以去官网阅读相关文档,本文这里不做太多探讨;
要换MVVM?
既然MVVM
有那么多好处,为何不换?
在移动开发领域,MVVM
提出也不短了,可大部分项目还是使用MVC
,只有少部分项目会用;还有一部分项目会有专门的架构师,为项目设计架构,他们会搞出一些大家都不懂的东西,然后在某个分享会上分享给大家;
但是!
我们要想清楚,你的项目真的需要MVVM
么?
不一定吧?
如果你的项目本来就不复杂,为何要换?MVC
就挺好的,别听那些"分享"
笔者不是说那些分享不好,好,确实好,但,那是在他们的项目上用呀,他们都是就职于BAT
这样的大厂的
一笑而过?
通过上面的阅读,相信你已经对MVC
和MVVM
有所了解,那么问题来了.
该用什么?
相信,很多iOS
开发者在找工作面试的时候都被问过MVVM
,基本上都快成了必问点了
我们不好说,哪个更好,哪个更适合.根据自己的情况选择
MVVM
的学习路线相对要难很多,但是空闲的时候尝试一下也是个不错的实践,其中收获必定不小的
至少下次吹牛或面试的时候,你也多一些谈资不是?
生命不息,折腾不止...
I'm not a real coder, but i love it so much!
网友评论