深入浅出MVC模式

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑(来自百度百科)


MVC模型

简介

MVC是一个基本模型,用于分类程序中的所有对象到三个阵营中的任意一个。

  1. 模型(Model)
    Model = What your application is (but not how it is displayed)

    模型(Model),模型层是程序的行为的合集

  2. 视图(View)
    View = Your Controller's minions

    视图(View),视图层是控制器(Controller)的辅助类,我们在构建我们的界面时会用到它,在视图(View)中的内容是相当通用的界面元素

  3. 控制器(Controller)
    Controller = How your Model is presented to the user(UIlogic)

    控制器(Controller),控制器(Controller)的作用给View解释并格式化这些来自模型(Model)的数据

通信

正确的MVC不仅仅知道内容的存放位置而且需要知道三层是如何通信的,接下来我们总结一下这三层是如何通信的。
(双黄线和白色虚线表示通信方式)

控制器与模型的通信:

1. 控制器(Controller) -> 模型(Model):完全控制权

Controller可以了解Model的一切行为,并且必须完全有能力与Model通信,根据Controller的需求使用Model公开的接口(API),因为Controller的职责就是通过View想用户展示Model中的数据,因此Controller拥有完全控制权限可以与Model通信。

2. 模型(Model) -> 控制器(Controller):Notification(通知)和KVO(键值对观察)

通常情况下,Model是不能与Controller通信。
场景:Model中的数据发生了变化,而Controller需要知道这种变化,如:数据改变了,数据库改变了。
问题:Model如何与Controller通信告诉他这些变化?
答案:Model会用一种电台的概念,将信息广播给想知道的任何Controller,在IOS中这种技术叫做Notification(通知)和KVO(键值对观察),所以Model的任务就是当数据发生变化是,通过Notification(通知)和KVO(键值对观察)广播一下,随后Controller会接收到来自广播的信息,他会发现数据在变化,他会再与Model通信获取改变后的数据

控制器与视图的通信

1. 控制器 -> 视图:完全控制权

Controller是可以访问View的,因为控制器负责通过自身对象向View发送指令,View是Controller向用户展示界面的一种方式,因此Controller可以控制View做任何它想做的事,这里会提到Outlet的概念(Outlet是控制器的一个属性,用于指向视图)。我们创建一个Outlet实际上就是实例化一个View到Controller中,这样我们的Controller可以发送指令到对应View了

swift
class DemoViewController: UIViewController {
    @IBOutlet weak var table: UITableView!
}

如上代码:UITableView作为一个视图,table即Outlet(DemoViewController的一个指向UITableView的属性),这样DemoViewController就具有了对UITableView的控制权了
2. 视图 -> 控制器:代理(Delegate)和数据源(DataSource)

因为View是通用的,他们不能真正的了解调用它们的Controller,所以他们只能盲目的方式与Controller通信
问题:视图上有个按钮(UIButton)它能跟Controller通信吗?
答案:可以,但是要注意,因为View是通用的,他们不能真正的了解调用它们的Controller,所以他们只能盲目的方式与Controller通信。一种大家都认同的,View与Controller通信的方式(Target操作和代理)

方式一: addTarget操作

控制器(Controller)本身写一个目标方法(target),然后给View的行为方法(action),并告诉View当你想做执行操作(比如你是一个按钮被点击,或者滑动条被滑动)给控制器(Controller),你可以通过此行为方法(action)向Controller发送消息,通过这种方式,通用的按钮或者拖动条就可以反过来与Controller通信了,它根本不需要知道调用的它控制器到底是什么类型的Controller,他只需要知道某一个事件在他身上触发时他就要通过行为方法(action)想目标方法(target)发送一个消息,就是这样一个盲目的、简单的、结构化的方式来作为视图与控制器之间的通信。还是代码比较直观`(∩_∩)′

swift
    class DemoViewController: UIViewController {
        @IBOutlet weak var btn: UIButton!
        override func viewDidLoad() {
            btn.addTarget(self, action: "hello:", forControlEvents: .TouchUpInside)
        }
        func hello() {
            print("Hello World!"
        }
    }

这样是不是很好理解:在DemoViewController中添加一个target:hello(),然后通过addTarget给View对象btn添加action。当你点击按钮时,就是打印:Hello World!

方式二:代理(Delegate)和数据源(DataSource)

代理(Delegate):
假如有时发生在视图(View)上的事件是比较复杂的,理解这个的方法是我通过几个关键字(will、should、did)描述这些情况:

  • 假如我们的View是一个滚动视图(ScrollView),用户按住它,将要滑动,它想要Controller知道用户将要执行滑动操作(will)
  • 假如它正在滚动,并且它想让Controller知道用户刚刚做了滚动操作(did)
  • 又或者用户按住屏幕滚动视图(ScrollView)需要知道是否允许这里执行滚动操作(should)

所有这些情况,滚动视图(ScrollView)本身或许没有足够的逻辑知道那些问题的答案,那它需要做的就是代理给Controller对象来回答这些问题(will,should,did,this,that或者其他事情例如:允许滑动、滑动到一点等等等...),你将要在那些代理协议中看到他们。这里我们需要在控制器中代理这些协议(盲目通信)
依旧是代码才会有更好的直观性,还记得在我们文章(一)文章(二)需要给UITableView做代理吗?其实我介绍了两种方式,我更偏向第二种:拓展方式,代码逻辑更清晰

swift
extension DemoViewController: UITableViewDelegate {
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
    {
        let index = indexPath.row
        let string = self.dataList[index]
        if let closure = self.myClosure {
            closure(tag: self.myTag!,string: string)
        }
        self.dismissViewControllerAnimated(true, completion: nil)
    }
}

上述代码:控制器:DemoViewController代理了视图UITableView的didSelectRowAtIndexPath协议,告诉视图我选中了某一行时需要执行的操作。
数据源(DataSource):
视图(View)不应该持有展示他们的数据,如果持有数据就违背了视图(View)的通用性了,换句话说:数据不应该作为视图的内部属性。
场景: 例如你要展示iPhone上的所有歌曲,你可能有10000首歌曲,另外你有一些通用的视图列表在你的视图(View)中,你不能传递这10000首歌给他实例变量中,并期望它来持有10000首歌以便你去浏览他们。
原因:
- 这种方式不高效;
- 这10000首歌属于哪一层?应该是Model。
你的音乐数据库本身就是一个Model(它与UI无关),视图仅仅是歌曲、艺术家、专辑和其他信息的一个列表 ,Controller必须通过Model查询数据并告诉一个View如何展示这些歌曲,所以我们需要通信让View展示数据,这些通信如何发生?答案:使用另一个种特殊的代理——数据源(DataSource),数据源不做之前代理(will、should、did……)的事。数据源(DataSource)做类似:数量:(有多少首歌曲),Controller查找Model将10000返回给视图,然后视图(View)为这些歌曲开辟内部空间。
当你在底部滚动它,他就开始发消息给控制器加载10条记录(返回150行后面的10条数据),控制器再次跟Model通信,并获取接下来的10条数据,于是控制器(Controller)通过这种盲目方式提供数据给视图,这就是视图(View)如何通过Model得到数据是通过这种不明确的、没有针对性的、结构化的方式。
所以数据源(DataSource)就是一个特殊类型的用于获取数据的代理,像列表视图(UITableView)他就有一个数据源和一个通常的代理
依旧是代码:上面我们给UITableView做了代理,接下来我们为UITableView绑定数据源
swift
extension DemoViewController: UITableViewDataSource {
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.dataList.count
    }
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier(self.cellId, forIndexPath: indexPath) as! DemoListCell
        //cell.cellImg.image = UIImage(named: powerData[indexPath.row][2])
        cell.cellLabel.text = self.dataList[indexPath.row]
        return cell
    }
}

总结:OK!这就是View: UITableView跟控制器(Controller)的通过一种不明确的结构化方式:分为代理(Delegate)和数据源(DataSource)

模型与视图的通信:不能(Never)

  • 很明显,模型(Model)是完全独立于UI的,模型没有任何办法向视图对象发送只送指令,也不能和视图层的任何对象通信,因为视图对象是基本的UI对象,他们是某种程度上是通用的,但是也是基本的UI对象,既然视图对象是通用的对象,他们就不能和任何特定的Model对象通信,他们需要一个控制器来传达信息。一句话:模型(Model)与视图(View)之间在没有任何通信

重要体会:

最后你会体会到,IOS开发框架本就是MVC模式,我们常用的控件无一不是视图,我们创建的ViewController其实都是控制器,我们实例化一个控件,就是为我们的Controller创建一个Outlet,并对其显示和控制,说到Model,CoreData就是我们的Model,原来我们一直都在MVC的世界里摸索到底什么才是MVC,多么痛的领悟!

名词解释:

  • M(Model):模型
  • V(View):视图
  • C(Controller):控制器
  • Outlet :控制器指向视图的属性
  • Notification:通知
  • KVO(Key-Value Observing):键值对观察
  • Delegate:代理,视图向控制器通信,通过 will、should、did等方式统称为代理
  • DataSource:数据源,一种特殊类型的代理,取决于视图是否需要显示大量数据
  • protocol:协议,在编程中含义:我的理解通过一种盲目(Blind)的、没有针对性的方式与其他对象通信。

多个MVC

如果是一个比较大型的复杂的项目呢?在屏幕上有三四个不同的区域都在发生着什么,我们可以结合多个MVC



文/zZ爱吃菜(简书作者)
原文链接:http://www.jianshu.com/p/b6367602dd72
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
深入浅出设计模式 pdf 深入浅出设计模式 中文版 设计模式 下载说明: 因个人上传资源大小只能是20M,而该书的PDF格式过大,所以只能分开上传。如有不便请谅解。【该书一共13章,在上传的时候我尽量按章节顺序打包了。有一个例外:其中第8章和第10章打包在一起,第9章太大单独打包了】 个人认为。此书确实是一本好书! ----------------------------------------------- 书籍介绍: 本书是一本通俗易懂的设计模式入门指导图书。   作者用C#和Java两种语言,借助现实生活范例和图片演示,全面阐释GRASP及GoF 23种设计模式的概念及其编程应用,帮助你领悟设计模式的思想及精华,并将其融会贯通、灵活应用到自己的开发过程中。   全书用两章篇幅对设计模式和GRASP作了基本介绍,用三章的篇幅全面展开对23种设计模式的讲解:对于每一种模式,先给出定义,接着通过类比方式用一个现实世界中的例子说明模式的应用,然后分别以C#和Java代码例述模式的架构实现。最后一章给出了两个设计模式综合案例,为读者实践设计模式提供了很好的学习环境。附录部分精心安排了自测题及答案,供读者练习并检验学习效果。   本书适合程序开发人员阅读,尤其适合作为大学计算机专业高年级学生和研究生的教学参考书。 ------------------------------------------------ 下载说明: 因个人上传资源大小只能是20M,而该书的PDF格式过大,所以只能分开上传。如有不便请谅解。【该书一共13章,在上传的时候我尽量按章节顺序打包了。有一个例外:其中第8章和第10章打包在一起,第9章太大单独打包了】 个人认为。此书确实是一本好书!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值