iOS 设计模式MVC、MVVM初探

MVC 与 MVVM

MVC

在这里插入图片描述
说起MVC,必须拿斯坦福大学公开课上的这幅图来说明,这可以说是最经典和最规范的MVC标准。

MVC在iOS中的实现思路: 一句话描述就是Controller负责将Model的数据用View渲染展示。

MVC如何产生

APP的实质就是界面与数据的交互。

  • 需要类负责界面的渲染展示,于是有了View
  • 需要类负责管理数据,于是有了Model
  • 我们设计的View应该能显示任意的内容,比如UILabel显示的文字应该是任意的而不只是某个特定Model的内容,所以我们不应该在View的实现中去写和Model相关的任何代码(任何代码指的是:修改Model的逻辑不应该在View里实现,使用Model的数据渲染视图是允许的)),如果这样做了那么View的可扩展性就相当低了。
  • 同样Model只负责处理数据,不处理界面的交互,所以Model中不应该写任何View相关代码(同样指的是:Model中不应该实现View的交互逻辑)
  • 数据和界面应该同步,也就是一定要有个地方要把Model的数据赋值给View,而Model内部和View的内部都不可能去写这样的代码,所以只能新创造一个类出来了,取名为Controller。

MVC是如何工作的

上图把MVC分为三个独立的区域,并且中间用了一些线来隔开,C和V以及C和M之间的白线,一部分是虚线一部分是实线,这就表明了引用关系:C可以直接引用V和M,而V和M不能直接引用C,而V和M之间则是双黄线,它们俩谁也不能引用谁,你既不能在M里面写V,也不能在V里面写M。

1.View与ViewController的交互

视图层最常见的事件就是按钮点击事件,实际上处理这个事件的应该是Controller,所以View把这个事件传递给了Controller。如何传递的呢,从图可以看到View上面的action,代表是事件,Controller上面的target,就是靶子,View究竟要把事件传递给谁?它被规定了传递给靶子,Controller实际上就是靶子,View只负责传递事件,不负责关心靶子是谁。这是V和C的一种交互方式,也是Target-Action 目标-动作对设计模式。
旁边还画出了V对C的另一种传值:协议-委托。委托有两种:代理和数据源。什么是代理,就是专门处理should、will、did事件的委托,什么是数据源,就是专门处理data、count等等的委托。

总结一下,就是主要通过三种方式:

  • action-target用来负责传递特定的事件; V->C的交互
  • dataSource-protocol用来通过回调的形式动态通过数据绘制界面; C->V的交互
  • delegate-protocol提前约定了对一些事件的处理规则,当被规定的事件发生后,就按照协议的规定来进行处理。协议委托可以通过协议方法的参数由V向C传值。比如cell点击事件的协议方法,tableView通过indexPath参数告诉C是哪个cell被点击了。 V->C的交互

2.Model与ViewController的交互

Model是数据管理者,数据可以是本地的、也可能是服务器的。拿一个简单的需求作为例子,例如我想在一个UILabel中显示一段文字,文字是网络请求获得的。

使用MVC设计模式:C中需要一个V(UIButton)作为属性显示这段文字,文字从M中获取,获取的地方在哪里呢?通常在C的生命周期里面,往往是viewDidLoad方法中调用M获取数据的方法来获取数据。M获取的数据是异步网络请求获得的,网络请求结束后,C才应该用M的数据赋值给V,那么现在如何知道网络请求结束了?
OC中有一种机制来解决这个问题“一个对象想要关系另一个对象的属性是否放生变化”的问题,KVO

KVO全称为Key Value Observing,键值监听机制,由NSKeyValueObserving协议提供支持,NSObject类继承了该协议,所以NSObject的子类都可使用该方法。让一个对象去观察另一个对象的某个键值路径所代表的属性,一旦发生了变化,那么系统会调用观察者的方法observingValueKeyForPath: 如果想在C中M的data属性发生改变之后刷新界面,那么就只需要向M添加观察者C,观察路径为@“data”。对C来讲,一旦M的data属性发生了变化,那么C的observingValueKeyForPath方法就会被调用,在方法中实现数据的传递,达到M和V同步的效果。

MVVM

MMVM:Model、View、ViewModel。与MVC相比,多了一个ViewModel少了一个Controller。VM的意义在于数据。
Model负责对数据进行存和取,然后我们对数据除了存取,还需要解析操作。

MVVM的诞生

举个例子:网络请求获取字典,字典作为原始数据放在Model中,Controller需要使用字典某可以key对应的一个数组,然后来用于UITableView的显示。

@objc func refreshAction(_ sender: UIRefreshControl) {
    
    
    self.model.data.removeAll()
    AF.request("https://api.seniverse.com/v3/weather/now.json?key=SJSVidYf39dI84d0s&location=shanghai&language=zh-Hans&unit=c").responseJSON {
    
     (response) in
        if let value = response.value {
    
    
            let json = JSON(value)
            if let result = json.dictionary?["results"]?[0] {
    
    
                if let dict = result.dictionary {
    
    
                    for key in dict.keys {
    
    
                        if let value = dict[key]?.dictionaryObject {
    
    
                            for key in value.keys {
    
    
                                let title = key;
                                let value = value[key];
                                let model = Model(title: title, detailTitle: value as! String)
                                self.model.data.append(model)
                                self.tableView.reloadData() // 刷新UI
                            }
                        }
                    }
                }
            }
        }
    }
    sender.endRefreshing()
}

在这里插入图片描述

我们往往把数据解析的工作放在了Controller中,在之前分析的MVC如何分配工作一样,需要数据有了M,需要界面有了V,需要有一个地方同步M和V于是有了C,我们忽略了M的进一步处理数据解析的过程。

在MVC出生的年代,手机APP的数据往往都比较简单,没有现在那么复杂,所以那时的数据解析很可能一步就解决了,所以既然有这样一个问题要处理,而面向对象的思想就是用类和对象来解决问题,显然V和M早就被定义死了,它们都不应该处理“解析数据”的问题,理所应当的,“解析数据”这个问题就交给C来完成了。

而现在App功能越来越复杂,数据结构越来越复杂,解析数据就没那么简单,如果继续按照MVC的设计模式,将数据解析工作放在Controller里面,Controller就显得极为臃肿。然后Controller期初设计的目的并不是处理数据。

Controller的工作:

  • self.view用来作为所有视图的容器
  • 管理自己的生命周期
  • 处理Controller之间的跳转
  • 实现Controller容器

而数据解析的任务并不应该由Controller来实现,M、V、C都不应处理数据,那么由谁来负责呢?开发者为数据解析新创建一个类ViewModel。这就是MVVM的诞生。

如何实现MVVM

现在我们开始着手实现MVVM之前,我先简单提一下之前遗留的一个问题:为什么MVVM这个名字里面,没有Controller的出现(为什么不叫MVCVM,C去哪了)。
你只需要记住两点:

1、Controller的存在感被完全的降低了;

2、VM的出现就是Controller存在感降低的原因。

在MVVM中,Controller不再像MVC那样直接持有Model了。

想象Controller是一个Boss,数据是一堆文件(Model),如果现在是MVC,那么数据解析(比如整理文件)需要由Boss亲自完成,然而实际上Boss需要的仅仅是整理好的文件而不是那一堆乱七八糟的整理前的文件。

所以Boss招聘了一个秘书,现在Boss就不再需要管理原始数据(整理之前的文件)了,他只需要去找秘书:你帮我把文件整理好后给我。

那么这个秘书就首先去拿到文件(原始数据),然后进行整理(数据解析),接下来把整理的结果给Boss。

所以秘书就是VM了,并且Controller(Boss)现在只需要直接持有VM而不需要再持有M了。

如果再进一步理解C、VM、M之间的关系:因为Controller只需要数据解析的结果而不关心过程,所以就相当于VM把“如何解析Model”给封装起来了,C甚至根本就不需要知道M的存在就能把工作做好。

那么我们MVVM中的持有关系就是:C持有VM,VM持有M。

所以在实现MVVM中一种必要的思想就是:

一旦在实现Controller的过程中遇到任何跟Model(或者数据)相关的问题,就找VM要答案。

猜你喜欢

转载自blog.csdn.net/WxqHUT/article/details/107235953