实际案例讲解iOS设计模式——MVC模式

MVC模式是iOS编程中提到的最多次的设计模式,也是使用最频繁的设计模式之一。网络上有很多的MVC模式的分析文章,但都是从原理上来解释,很少能找到配套的案例来说明到底在实际的项目中要如何的使用这种模式。小编在经过详细的研究、对比和实验了之后,总结了一下这个模式的一些简单使用方法,希望能起一个抛砖引玉的作用,使得对MVC默认的同学能依葫芦画瓢的了解MVC模式的使用方法,并以此类推出更多、更好的方法出来。

这篇文章先从老生常谈的MVC设计模式的原理说起,然后配上一个简单的案例,以演示如何将一个常规的编程改为使用MVC模式来编程。

本文假定,您已经熟悉了target-action模式、delegate模式、Notification或KVO模式。如果这几个模式还不是很了解,可参考网络上其它文章说明,本文不会就这些模式做详细讲解。

一、什么是MVC?

MVC是model-view-control的简称。在解释这个名词之前,我们先假定一个案例,在这个例子中,有两个文本框T1和T2、两个按钮B1和B2,B1为保存按钮,B2为加载按钮;T1为文本内容编辑按钮,当点击B1时,将T1里面的内容保存到数据库中;当点击B2时,将数据库中保存的内容显示到T2上。为了验证这个操作是否是经过数据库了,我们可以做一个操作,比如将T1里面的内容后面加上一些字符、符号等(这个相信很好做吧,你可以在将T1的数据保存到数据库的时候做这个操作,也可以在将数据从数据库中调出、显示在T2上之前做这个操作,本案例不在详细叙述)。当我们脑海里形成好了这样一个应用之后,下面我们来看看M、V、C到底做什么样的工作:

View——顾名思义,就是存放视图使用的。对应上面的例子,我们应该把T1、T2、B1和B2放在View上,对吧?

Model——即模型。模型一般都有很好的可复用性,统一管理一些数据。在上面的例子中,数据库是不是可以作为一个模型呢?答案是肯定的。所以,我们就把数据库的所有操作都放在Model里面执行——包括但不限于数据库的创建、插入、查询、更新和删除(为啥都放一起?地球人都知道。。。)

Controller——控制器,充当一个CPU的功能,即该应用程序所有的工作都由Controller统一调控。它负责处理View和Model的事件。具体怎么调控和处理?在下面的MVC原理里面,我们将详细讲解。

MVC模式能够完成各司其职的任务模式,由于降低了各个环节的耦合性,大大优化Controller的代码量,当程序调试时,如果某一个功能没有按照既定的模式工作,可以很方便的定位到到底是Controller还是View还是Model出了问题,而且还利于程序的可复用性,建议在程序中能多多使用这个模式。

二、MVC的原理

上面的内容中,已经详细描述了model、view和controller之间如何各司其职(即该是谁的东西,谁就要保护好,不能让另外一个越俎代庖的去处理)。MVC模式虽然是iOS编程中使用最广泛的模式,但论起复杂程度,MVC模式可以算是众多设计模式之首。通常情况下,MVC模式需要综合使用target-action模式、delegate模式、Notification或KVO模式等。下图是斯坦福大学的iOS一堂关于iOS介绍的公开课上所使用的示例图,这张图像也生动的描绘出来了MVC模式的工作原理,接下来的原理讲解也是依托于这张图像:

1、 Controller和View之间可以通信,Controllor通过outlet(输出口)控制View,View可以通过target-action、delegate或者data source(想想UITableVeiwDatasource)来和Controller通信;

2、 Controller在接收到View传过来的交互事件(View就是完成让人和程序的交互的呀,比如按B1按钮)之后,经过一些判断和处理,把需要Model处理的事件递交给Model处理(比如刚才的例子中的保存到数据库),Controller对Model使用的是API;

3、 Model在处理完数据之后,如果有需要,会通过Notification或者KVO的方式告知Controller,事件已经处理完,Controller再经过判断和处理之后,考虑下一步要怎么办(是默默无闻的在后台操作,还是需要更新View,这得看Controller的“脸色”行事)。这里的无线天线很有意思,Model只负责发送通知,具体谁接收这个通知并处理它,Model并不关心,这一点非常重要,是理解Notification模式的关键。

4、 Model和View之间不直接通信!

按照上面的原理,我们知道了M、V、C之间的各司其职——Model不保存控件,View不做数据库操作(但这个也不是绝对,如果需要View做一些数据缓存工作,还是需要保存一些临时数据的),而Controller就充当了两者之间的协调器。

如此,MVC的原理已经理出来一个头绪了,那么我们来看一个实际的例子,来验证如何使用MVC模式。在这个例子中,View通过target-action模式向Controller传递消息,Controller通过API调用Model里面的方法来处理从View那接收到的消息;Model处理完数据之后,通过Notification模式向Controller传递一个消息,最终Controller通过一个方法(即Notification的接收方法)弹出来一个对话框显示Model已经处理完成。

三、实际案例

1、 不使用MVC模式的案例:

1.1 使用Xcode创建一个Single View Application,命名为MVCsample。

1.2 在ViewController.h里面,添加如下代码:

[objc] view plaincopy

  1. @property (nonatomic, strong) UIButton *saveBtn; //点击该按钮,保存数据
  2. @property (nonatomic, strong) UIButton *loadBtn; //点击该按钮,加载数据

如下图所示:

1.3 在ViewController.m文件的- (void)viewDidLoad方法中,添加如下代码:

[objc] view plaincopy

  1. _saveBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  2. [_saveBtn setFrame:CGRectMake(50, 50, 150, 80)];
  3. [_saveBtn setTitle:@"保存" forState:UIControlStateNormal];
  4. [_saveBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];  //默认的页面背景色是白色,butotn上的文字的默认颜色也是白色,所以在此处将button上的文字颜色设置为黑色,以便显示
  5. [_saveBtn addTarget:self action:@selector(saveBtnPressed:) forControlEvents:UIControlEventTouchUpInside];  //添加target-action模式
  6. [self.view addSubview:_saveBtn];
  7. _loadBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  8. [_loadBtn setFrame:CGRectMake(50, 160, 150, 80)];
  9. [_loadBtn setTitle:@"加载" forState:UIControlStateNormal];
  10. [_loadBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  11. [_loadBtn addTarget:self action:@selector(loadBtnPressed:) forControlEvents:UIControlEventTouchUpInside];  //添加target-action模式
  12. [self.view addSubview:_loadBtn];

如下图所示:

1.4 在上面的一行代码中,我们分别为_saveBtn和_loadBtn添加了target-action模式,接下来在ViewController.m文件中实现这两个action方法,代码如下:

[objc] view plaincopy

  1. - (void)saveBtnPressed : (UIButton*)sender{
  2. NSLog(@"保存");
  3. NSLog(@"当前设备的型号:%@", [[UIDevice currentDevice]systemVersion]);
  4. /*
  5. //从iOS 8开始,苹果建议不使用UIAlertView和UIActionsheet,而是要使用UIAlertController。从iOS 9开始,UIAlertView和UIActionsheet更是已经被禁用,所以在这个案例中,虽然如下的UIAlertView方法还是可以照常运行,但我们还是遵从苹果的建议,使用新的技术和方法比较妥当
  6. UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"恭喜" message:@"保存成功" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
  7. [alert show];
  8. */
  9. UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"恭喜"
  10. message:@"保存成功"
  11. preferredStyle:UIAlertControllerStyleAlert];
  12. UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
  13. NSLog(@"点击了取消按钮");
  14. }];
  15. UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
  16. NSLog(@"点击了确定按钮");
  17. }];
  18. [alertController addAction:cancelAction];
  19. [alertController addAction:okAction];
  20. [self presentViewController:alertController animated:YES completion:nil];
  21. }
  22. - (void)loadBtnPressed : (UIButton*)sender{
  23. NSLog(@"加载");
  24. }

代码有点长,不再截图,各位请自行想象~~~

1.5 然后运行该程序,模拟器显示如下图:

点击了“保存按钮”后,模拟器及Xcode输出台显示如下图所示:

再点击AlertView上的“取消”或者“确定”按钮,Xcode输出台显示如下:

以上内容,就是不使用MVC模式的情况下,我们经常做得内容。下面,我们就按照MVC的原理,来将改程序按照MVC的思想重新做一遍。

2. 使用MVC模式的案例:

2.1 使用Xcode创建一个Single View Application,命名为MVCsampleWithMVC;

2.2 新建三个文件夹(Group),分别命名为M、V和C;

2.3 新建一个名为VView的类,继承自UIView,并将UIView.h和UIView.m文件拖到V文件夹下;

2.4 新建一个名为MModel的类,继承自NSObject,并将MModel.h和MModelm文件拖到M文件夹下;

2.5 将ViewController.h和ViewController.m文件拖到C文件夹下。做完2.2~2.5的工作之后,Xcode工作组应该显示如下图所示:

2.6 按照MVC的思想,V里面只存放界面显示的控件,在刚才的例子中,就是“保存”和“加载”这两个按钮,于是,我们把有关这两个按钮的代码都写到VView.h和VView.m中。

写好后的VView.h的代码如下:

[objc] view plaincopy

  1. #import <UIKit/UIKit.h>
  2. @interface VView : UIView
  3. @property (nonatomic, strong) UIButton *saveBtn;  //点击该按钮,保存数据
  4. @property (nonatomic, strong) UIButton *loadBtn;  //点击该按钮,加载数据
  5. - (void)viewInit;  //添加一个方法,用于初始化控件
  6. @end

写好后的VView.m的代码如下:

[objc] view plaincopy

  1. #import "VView.h"
  2. @implementation VView
  3. - (void)viewInit {
  4. _saveBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  5. [_saveBtn setFrame:CGRectMake(50, 50, 150, 80)];
  6. [_saveBtn setTitle:@"保存" forState:UIControlStateNormal];
  7. [_saveBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];  //默认的页面背景色是白色,butotn上的文字的默认颜色也是白色,所以在此处将button上的文字颜色设置为黑色,以便显示
  8. [self addSubview:_saveBtn];
  9. _loadBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  10. [_loadBtn setFrame:CGRectMake(50, 160, 150, 80)];
  11. [_loadBtn setTitle:@"加载" forState:UIControlStateNormal];
  12. [_loadBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  13. [self addSubview:_loadBtn];
  14. }
  15. @end

和上面的不使用MVC模式例子相比较,发现_saveBtn和_loadBtn都没有添加target-action方法。这是因为target-action方法需要设置一个target对象,在这个对象里调用action方法。按照MVC的思想,V里面不进行数据处理,而是要在C里面统一调控有C还是M来处理数据。本例中,我们是在C里面处理,所以我会把targe-action方法写到C里面,详细后面介绍。

2.7 按照MVC的思想,M主要用于数据的处理,我们假设这个案例是要将V中的某一段内容保存到数据库中,那么这个保存到数据库中的操作就是在M里面完成的。此处我们直接简化操作,只是写两个方法,用来提示保存和加载成功的。如此,MModel.h中的代码如下:

[objc] view plaincopy

  1. #import <Foundation/Foundation.h>
  2. @interface MModel : NSObject
  3. - (void)save;
  4. - (void)load;
  5. @end

MModel.m中的代码如下:

[objc] view plaincopy

  1. #import "MModel.h"
  2. @implementation MModel
  3. - (void)save{
  4. NSLog(@"保存。。。");
  5. [[NSNotificationCenter defaultCenter] postNotificationName:@"saveSucessful" object:self];  //使用Notification模式发送一个通知,用于通知Controller要做什么事情
  6. }
  7. - (void)load{
  8. NSLog(@"加载。。。");
  9. }
  10. @end

可以看到,这段代码和不使用MVC模式的代码中的target-action方法中的action方法是基本上一样的。那么我么就会有一个思路——在Controller中,当我们为按钮添加了target-action模式之后,对应要实现的action方法里面,是不是只需要调用MModel.h里面的对应的- (void)save和- (void)load方法就行了呢?完全正确!看,这就是C通过API调用M!

在- (void)save方法中,我还使用Notification模式发送了一个通知,这个通知用来告知Controller,我已经保存好数据了,接下来你看着办!在上一个不使用MVC模式的例子中,Controller是弹出来一个Alert,本例子中,我们也要实现这个功能。

2.8 V和M都已经分配好了,接下来就是看C如何协调分配了。ViewController.h中的代码如下:

[objc] view plaincopy

  1. #import <UIKit/UIKit.h>
  2. #import "VView.h"
  3. #import "MModel.h"
  4. @interface ViewController : UIViewController
  5. @property (nonatomic, strong) VView *aView;  //实例化一个VView的对象
  6. @property (nonatomic, strong) MModel *mModel;  //实例化一个MModel的对象,以便于调用MModel中的方法
  7. @end

由于我在MModel.h中定义的方法都是实例方法,所以我们只能实例化一个MModel的对象来调用这些方法。如果将MModel中的方法设置为类方法或者单例模式,就可以直接用MModel这个类来调用了。

2.9 ViewContro.m中的代码如下:

[objc] view plaincopy

  1. #import "ViewController.h"
  2. #define deviceScreenWidth [[UIScreen mainScreen]bounds].size.width
  3. #define deviceScreenHeight [[UIScreen mainScreen]bounds].size.height
  4. @interface ViewController ()
  5. @end
  6. @implementation ViewController
  7. - (void)viewDidLoad {
  8. [super viewDidLoad];
  9. [[NSNotificationCenter defaultCenter] addObserver:self
  10. selector:@selector(saveOK:)
  11. name:@"saveSucessful" object:nil];  //添加一个通知方法,当这个Controller接收到一个名称为@"saveSucessful"的通知后,就执行saveOK:方法
  12. _aView = [[VView alloc]initWithFrame:CGRectMake(0, 0, deviceScreenWidth, deviceScreenHeight)];  //初始化时一定要设置frame,否则VView上的两个按钮将无法被点击
  13. [_aView viewInit];
  14. [_aView.saveBtn addTarget:self action:@selector(saveBtnPressed:) forControlEvents:UIControlEventTouchUpInside];  //为“保存”按钮添加target-action模式
  15. [_aView.loadBtn addTarget:self action:@selector(loadBtnPressed:) forControlEvents:UIControlEventTouchUpInside];  //为“加载”按钮添加target-action模式
  16. [self.view addSubview:_aView];
  17. _mModel = [[MModel alloc]init];
  18. }
  19. - (void)saveBtnPressed : (UIButton*)sender{
  20. [_mModel save];  //调用MModel.h中的方法(API)
  21. }
  22. - (void)loadBtnPressed : (UIButton*)sender{
  23. [_mModel load];  //调用MModel.h中的方法(API)
  24. }
  25. - (void)saveOK : (NSNotification*) notification{
  26. UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"恭喜"
  27. message:@"保存成功"
  28. preferredStyle:UIAlertControllerStyleAlert];
  29. UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
  30. NSLog(@"点击了取消按钮");
  31. }];
  32. UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
  33. NSLog(@"点击了确定按钮");
  34. }];
  35. [alertController addAction:cancelAction];
  36. [alertController addAction:okAction];
  37. [self presentViewController:alertController animated:YES completion:nil];
  38. }
  39. - (void)didReceiveMemoryWarning {
  40. [super didReceiveMemoryWarning];
  41. // Dispose of any resources that can be recreated.
  42. }
  43. @end

代码里面有一些注意事项,我都用注释的形式写在了代码的后面,请读者自行研究判断,此处不再赘述,有疑问欢迎留言讨论。从C的代码中可以看到,如何在MVC中使用target-action模式(delegate模式和data source模式,暂时不在这个案例中讲述),如何在C中调用M中的API,以及M如何通过Notification模式向C发送通知并由C处理相关的通知。虽然M+V+C里面的代码总量比不使用MVC模式多了一些,但MVC模式写出来的代码层次分明,结构清楚,分工明确,为以后修改代码、调试程序都带来了极大的便利。比如你要修改显示的效果,只需要修改V中的就行,然后按照调理在C中添加相应的方法,多么明确。使用MVC模式的运行效果我就不再附图了,亲测程序能正常工作,请读者自己也试一试吧,手动写写代码,对理解代码会有很大的帮助的。

四、结语

至此,MVC模式的介绍就算结束了。可能很多读者对这么一个小程序就使用MVC模式觉得有点得不偿失,但万丈高楼平地起,只有从小处开始就能注意细节方面的事情,对以后写大型程序来讲,会非常的有帮助的。在编程界,一直流传有一个观点——一周不写代码,再写时就会觉得手生。其实,很多代码或者编程习惯,并不是靠脑袋记住的,而是靠习惯养成的。只有习惯了使用良好的设计模式,才能对设计模式的使用“聊熟于胸”,才能“信手拈来”。我也相信,MVC模式绝对不止这么一点点的思想和方法,希望有兴趣的读者朋友,能多多深入研究,多多与人探讨,将MVC的模式深挖掘,挖出它的灵魂。

本文如有纰漏,欢迎各位同仁指出并给出修改建议,为后来者提供更加准确的咨询。

——————————————————————————————————

欢迎关注我的微博:http://weibo.com/u/5750370715/home?wvr=5&c=spr_web_360_hao360_weibo_t001

欢迎关注我的微信公众号,您的支持,我的荣幸。微信搜索“登顶望峰”或者扫描如下图像二维码即可关注。我将会在公众号中不定时推送开发相关的技巧、咨询、业界新闻等。

时间: 2024-10-22 17:52:44

实际案例讲解iOS设计模式——MVC模式的相关文章

【IOS 开发】IOS 开发 简介 (IOS项目文件 | MVC 模式 | 事件响应机制 | Storyboard 控制界面 | 代码控制界面 | Retina 屏幕图片适配)

一. IOS 项目简介 1. IOS 文件简介 创建一个 HelloWorld 项目, 在这个 IOS 项目中有四个目录 : 如下图; -- HelloWorldTests 目录 : 单元测试相关的类和资源; (1) HelloWorld 目录 HelloWorld 目录介绍 : -- 命名规则 : 该目录名称与 IOS 项目名称相同, 是主目录; -- 存放内容 : IOS 项目的 源码文件, 界面设计文件, 资源文件都存放在该目录下; -- 源文件 : Objective C 的 .m 和

iOS的MVC模式

http://blog.devtang.com/blog/2012/02/05/mvc-in-ios-develop/ iOS的MVC模式,布布扣,bubuko.com

IOS常用设计模式——MVC模式(IOS开发)

Cocoa Touch中的MVC模式中,模型和视图不能进行通信,所有通信都是控制器完成. - 模型:是个较为特殊的对象,本质是视图的数据,Text Field输入的内容,Button上的标签都是模型,不一定非要创建一个模型类. - 视图:UIView,这个比较容易理解,向控制器发送请求. - 控制器:更新视图作为对用户请求的回应,更新模型的数据. 模型里面就是数据,视图就是个界面,控制器是中间的更新媒介. 1.触摸myButton时候,会触发方法. 2.视图控制器实现了一些委托和数据源的协议.

iOS里面MVC模式详解

MVC是IOS里面也是很多程序设计里面的一种设计模式,M是model,V是view,C是controller.MVC模式在ios开发里面可谓是用得淋漓尽致. 以下是对斯坦福大学ios开发里面MVC模式的一段话的翻译 主要的宗旨是把所有的对象分为3个阵营,model阵营,view阵营,或者是controller阵营 model(APP的目的) 举个例子,你要做一个打飞机的游戏,那么这个就是太空中这辆飞船的位置,什么机型,每个飞船有多少机枪,护甲有多少等等.这就是model所做的事,而飞机在屏幕上的

ios设计模式—策略模式

其实ios的设计模式有很多种,在实际应用中我们应该可以的去使用一些设计模式去规范和丰富我们的代码~~嘿嘿 其实策略模式就是将我们代码中的复杂逻辑抽象成一个对象去处理,这会大大减少代码量,并且去使我们的逻辑看起来更有条理. 拿if else为例可以将一个复杂的判断逻辑写到一个抽象的基类中,在其中进行判断再运用继承和多态的原理去分别执行不同的业务逻辑,这样会在控制器中减少代码量 控制器代码:通过传入判断条件去控制执行逻辑 // // ViewController.m // strategyDemo

iOS设计模式——生成器模式

选择建造自己的房子的人会把工程外包给承包商.单一承包商不能建造整个房子,他将其分解为几个部分,然后转包给几个实际的建筑商,他们懂得如何将零部件组装起来.房子由由风格.颜色和尺寸各不相同的部件组成.客户告诉承包商房子里都要有什么,然后承包商协调各房屋建筑商,决定需要做什么.应该如何建造,建筑商就如何施工.建房子是个复杂过程,单凭一双手就想建房子,即便可能也非常困难.如果承包商(指导者)与懂得如何建造的建筑商相互协调,这一过程简单得多且更易管理. 有时,构建某些对象有多种不同方式.如果这些逻辑包含在

iOS设计模式 - 命令模式

命令模式 前言: 命令对象封装了如何对目标执行指令的信息,因此客户端或调用者不必了解目标的任何细节,却仍可以对他执行任何已有的操作.通过把请求封装成对象,客 户端可 以把它参数化并置入队列或日志中,也能够支持可撤销操作.命令对象将一个或多个动作绑定到特定的接收器.命令模式消除了作为对象的动作和执行它的接收器之 间的绑定. 正文内容大纲: 1.电视机.遥控器与接收器之间的关系2.改变一个视图的明暗程度(未使用命令模式)3.命令模式介绍4.改变一个视图的明暗程度(使用命令模式)5.附录 命令模式 1

iOS设计模式——MVC(Model-View-Controller)

Modol View Controller(MVC)是一种最早的也是最成功的可重用的设计模式,70年代的时候首次在smaltalk编写的程序中成功使用.基于MVC设计模式,Cocoa整体架构可以划分为三个子系统:Model, View, Controller,我们可以相应的理解为:数据模型,视图和控制器. Modol View和Controller的角色 要想理解MVC设计模式中每个子系统的角色,我们就需要理解一个应用程序的功能和行为.我们知道一个应用程序通常需要存储数据信息,检索数据信息以及把

iOS设计模式---访问者模式

一个简单的Car模型,含有1台Engine.4个Wheel,使用访问者模式添加对Car的升级与维修操作. 定义Engine类: // //  NimoEngine.h //  VisitorDemo // //  Created by fu zheng on 15/8/12. //  Copyright (c) 2015年 FuZheng. All rights reserved. // #import <Foundation/Foundation.h> #import "NimoC