MVC模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。除此之外,此模式通过对复杂度的简化,使程序结构更加直观

  • 控制器(Controller)--> 负责转发请求,对请求进行处理。
  • 视图(View) --> 界面设计人员进行图形界面设计。
  • 模型(Model) --> 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。

以上出自维基百科资料,下面说点人为描述(简单易懂的)~

  • Model层: 数据处理层,包括网络请求,数据加工
  • View层: 所有App上看得到的界面
  • Controller层: Model 与 View层的中介,把Model数据在View上展示出来
  • 目的: 低耦合,可复用

先看这张图,这张图是iOS的MVC架构中最经常出现的图了吧,因为IOS中的ControlllerUIViewController,所以导致很多人会把视图写在Controller中,如下图:

@implementation DemoViewController

- (void)viewDidLoad {
[super viewDidLoad]; //setupUI //1.createView
UIView *view = [[UIView alloc]init];
view.frame = CGRectMake(100, 100, 100, 100);
view.backgroundColor = [UIColor orangeColor];
[self.view addSubview:view]; //2.createButton
UIButton *btn = [UIButton buttonWithType:UIButtonTypeInfoDark];
btn.center = self.view.center;
[self.view addSubview:btn]; //3...
}
复制代码

这种写法在我刚蹭到iOS的时候也这样写过,先说这样写的好处,以及初学者为什么会这么写:

  1. 比如按钮,可以在当前控制器直接add target:添加点击事件,在当前控制器内就能调用到点击方法,不需要设置代理之类的;
  2. 比如要找某个界面,直接切到这个界面对应的controller就行,因为View 写在 Controller里面,不用去别的地方找,就这里有;
  3. 比如一个View,里面有一张图片,图片依赖于网络资源,这样写的好处,可以直接让 ViewController 中就能拿到资源,不需要传值

缺点!!:

  • 导致Controller特别臃肿,里面代码特别多,视图一复杂起来,代码量可能过1000行,不好维护
  • 写在Controller里无法复用,除非你在 VC2里面 copy 当前VC中的 View的代码
  • 特别low!!会被懂架构的人瞧不起,喷你根本不是MVC,是MC架构,可能还要你来段喊麦证明一下自己(-。-)

如何告别MC模式,真正走到MVC

  1. 先给自己洗脑,iOSController不是UIViewController,而是普通的Controller,没有View。(很关键的一步)
  2. 模块化划分,每个模块对应自己的一个View,例如Demo2模块,View层里面有个Demo2View,将界面元素写到View中

知识1:如何传值(参数)

//View
+ (instancetype)viewWithTitleStr:(NSString *)titleStr{ //do createView
//...
} //Controller
@implementation DemoViewController - (void)viewDidLoad {
[super viewDidLoad]; /*setupUI*/
//1.createView - 参数通过`View`的函数作为外部参数传进去
DemoView *view = [DemoView viewWithTitleStr:@"我是参数"];
[self.view addSubview:view];
}
复制代码

知识2:控件点击事件如何回调给控制器

//View
@implementation DemoView - (instancetype)initWithTitleStr:(NSString *)titleStr{
if (self = [super init]) { UIButton *btn = [UIButton buttonWithType:UIButtonTypeInfoDark];
[self addSubview:btn];
[btn addTarget:self action:@selector(p_clickBtn:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
} - (void)p_clickBtn:(UIButton *)sender{ //通过代理回调
[_delegate respondsToSelector:@selector(clickBtn:)] ?
[_delegate clickBtn:sender] : nil;
} //Controller
@implementation DemoViewController - (void)viewDidLoad {
[super viewDidLoad]; //setupUI //1.createView
DemoView *view = [DemoView viewWithTitleStr:@"我是参数"];
view.delegate = self;
[self.view addSubview:view];
} #pragma mark - privateDelegate
- (void)clickBtn:(UIButton *)sender{
//View层按钮的点击事件回调~
}
复制代码



接下来看这张iOS MVC架构图二,这张也是特别常见,在上面解决了View层之后,我们来看下这里的Model层~

@implementation DemoViewController

- (void)viewDidLoad {
[super viewDidLoad]; //loadDatas
[[AFHTTPSessionManager manager]GET:url
parameters:parameters
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject)
{ //刷新tableView
_datas = responseObject;
[_tableView reloadDatas]; } failure:nil];
}
复制代码

这种写法在我刚蹭到iOS的时候又这样写过,先说这样写的好处,以及初学者为什么会这么写:

  1. 简单,网络请求完,直接在当前控制器刷新TableView的数据源
  2. 比如要找某个界面的网络请求,直接切到这个界面对应的controller就行,因为数据请求 写在 Controller里面,不用去别的地方找,就这里有;
  3. 比如当前网络请求接口,需要外部参数,比如前一个界面的uuid,这样写的好处,可以直接让当前请求在 Controller 中就能拿到资源,不需要传值

缺点!!:

  • 又导致Controller特别臃肿,里面代码特别多,如果当前控制器需要多次请求,代码量可能过1000行,不好维护
  • 写在Controller里无法复用,除非你在 VC2里面 copy 当前VC中的 网络请求的代码
  • 如果某些接口有依赖要求,接口1请求完再请求接口2,嵌套起来,辣眼睛的程度差点治好我多年的近视
  • 特别low!!会被懂架构的人瞧不起,喷你根本不是MVC,如果你还用了上面的View写在Controller的操作的话,恭喜你,最终大法 - Controller架构顺利完成,并不需要什么Model && View

如何告别VC模式,真正走到MVC

  1. 不用洗脑,给自己一个大耳刮子让自己清醒清醒,这iOSController就算是UIViewController,也没看到M啊,没有Model。(很关键的一步)
  2. 模块化划分,每个模块对应自己的一个Model,例如Demo2模块,View层里面有个Demo2Model,将网络请求&&数据处理写到Model

知识1:如何传值(参数)

@implementation DemoModel

+ (NSArray *)fetchDatasWithUUid:(NSString *)uuid{

    //Model发送网络请求
NSDictionary *parameters = @{@"uuid":uuid}
[[AFHTTPSessionManager manager]GET:url
parameters:parameters
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject)
{
//这是异步请求,无法return array
} failure:nil]; }
复制代码

知识2:如何回调(网络请求是异步请求) - 通过Block

//Model
@implementation DemoModel + (void)fetchDatasWithUUid:(NSString *)uuid success:(successBlock)block{ //Model发送网络请求
NSDictionary *parameters = @{@"uuid":uuid}
[[AFHTTPSessionManager manager]GET:url
parameters:parameters
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject)
{
//通过block异步回调~
block(responseObject); } failure:nil];
} //Controller
@implementation DemoViewController - (void)viewDidLoad {
[super viewDidLoad]; //loadDatas
[DemoModel fetchDatasWithUUid:_uuid success:^(NSArray *array) {
_datas = array;
[_tableView reloadDatas];
}];
}
复制代码

基础的MVC讲解完毕,其实本质上就是让Controller减压,不该控制器管的他别让他知道,如上基础MVC操作之后的优势:

  • MVC架构分明,在同一个模块内,如果视图有问题,找到该模块的View就行,其他同理,Controller代码大大减少,负责View的代理事件就可以
  • 可以复用,比如你一个产品列表的数据,首页也要用,产品页也要用,直接分别在其对应的VC1 && VC2 调用函数[ProductModel fetchDatas]即可,无需写多次,View的复用同理
  • 结构分明,便于维护,拓展也是在此基础上拓展,代码干净简洁。

进阶讲解 - MVC 配合 继承,进阶提高效率

  • 常用的方法,抽一个基类出来,继承是子类可以拥有父类的方法,重新父类的方法即可,无需声明
//数据基类
@interface MNBaseDatas : NSObject //请求数据成功
typedef void (^MNsuccessBlock)(NSArray *array); + (void)fetchDatasSuccessBlock:(MNsuccessBlock)block; + (void)fetchDatasSuccessBlock:(MNsuccessBlock)block
failureBlock:(MNfailureBlock)failure;
复制代码

如果,如果抽出一个数据模型的基类,比如这里的MNBaseDatas,如之前我们举例的DemoModel就无需声明

@interface DemoModel : MNBaseDatas

/**继承自MNBaseDatas,父类有的就可以不用声明,这里的block 和 类方法都可以不用声明*/
//typedef void (^successBlock)(NSArray *array); //+ (void)fetchDatasSuccessBlock:(MNsuccessBlock)block; @end //Controller
@implementation DemoViewController - (void)viewDidLoad {
[super viewDidLoad]; //loadDatas - DemoModel没有声明 -fetchDatasSuccessBlock,一样可以调用,因为父类有此方法
[DemoModel fetchDatasSuccessBlock:^(NSArray *array) {
_datas = array;
[_tableView reloadDatas];
}];
}
复制代码

如果父类没有的方法或属性,在子类里面写就行了,不会影响到父类代码,父类一般也是放公共,常用的方法(或属性),如果是特殊的,直接在子类里面新增即可,无需添加到父类~

控制器也可以使用继承,可以减少不少冗余代码

//基类控制器
@interface MNBaseViewController : UIViewController @property (nonatomic, weak)UITableView *tableView; @property (nonatomic, copy)NSArray *datas; - (void)setupUI; - (void)loadDatas; @end //MNBaseViewController.m 文件
@interface MNBaseViewController ()
<
UITableViewDelegate,UITableViewDataSource
> #pragma mark - setupUI
- (void)setupUI{
//统一创建tableView,设置当前代理=self
UITableView *tableView = [[UITableView alloc]init];
tableView.frame = Frame(0, DefaultNaviHeight, ScreenW, ScreenH - DefaultNaviHeight);
tableView.delegate = self;
tableView.dataSource = self;
}
复制代码
  • 根据我们的封装,基本上所有的控制器都需要设置界面 setupUI 获取数据 loadDatas,所以将这两个函数抽到基类MNBaseViewController
  • 因为iOS中,tableView应该算最常见的控件之一,基本上大多数界面都会用它展示数据,所以tableView也抽到基类中,当公告属性
  • tableView 就跑不了数据源了,datas 同理,也抽到基类
  • 同时,设置MNBaseViewController成为 tableViewdelegatedataSource,所有的子类都无需再声明
  • 如果有需要用到tableView的,一个[super setUI]就能拥有这个tableView,无需创建

这样,所有的UIViewController,只要继承自MNBaseViewController的,都可以有如上的函数和方法(可以根据需要扩充)

进阶的MNBaseViewController

//继承自`MNBaseViewController`
/*数据结构是 - @[],没有section的tableVIew*/
@interface MNBaseControllerTypeNoSection : MNBaseViewController @end /*数据结构是 - @[@[]],有section的tableVIew*/
@interface MNBaseControllerTypeHadSection : MNBaseViewController @end 复制代码
//实现
/**没有section的tableVIew**/
@implementation MNBaseControllerTypeNoSection - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { //没有section,直接返回数据源count
return self.datas.count;
}
@end /**有section的tableVIew**/
@implementation MNBaseControllerTypeHadSection -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ return self.datas.count;
} -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [self.datas[section] count];
} @end 复制代码

如上面的两个基类MNBaseControllerTypeHadSection,MNBaseControllerTypeNoSection,根据我们自己需要的数据源,选择继承自哪个类,他们拥有父类MNBaseViewController的所有属性,他们的子类,也都无需在写比如-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView之类的方法等,大幅度减少冗余代码~

如图我随机抽了几个界面出来,可能有部分人都有做过类似的界面,通过合理的架构,大部分控制器代码可能也就100行,详情可见Demo~




总结

对于架构来说,仁者见仁智者见智,每个人都有一套适合自己的,并不是说MVC有多low,MVVM甩用MVC 技术10086条街,主要还是根据项目,根据自己的使用慢慢进阶。

下面有我一个最近花了几个小时抽出来的Demo,当然实际开发中的,可能Controller的代码会多一些,因为有些点击事件的代码我都是封装调用的,再放进去感觉很容易让看的人跑偏,所以点击事件基本都注掉了。但是,秉着这种思想,其实我最近写了一个多重过滤袋滑动多控制器的界面,界面相对来说比较复杂,控制器代码也才200行,总的来说还算干净。

其实TableView也可以剥离到外部,不放在Controller中,我也有Demo是那么做的,后来发现没必要,感觉还特意封出去感觉有点画蛇添足,因为我这种架构,其实tableView很多方法都在基类控制器里面的,所以Controller中的tableView代码也不会多。

MVC架构实战-Demo


欢迎star~

iOS架构入门 - MVC模式实例演示的更多相关文章

  1. (转)浅析三层架构与MVC模式的区别

    MVC模式介绍: MVC全名是Model ViewController,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用于组织代码用一种业务逻辑和数据 ...

  2. Thrift入门及Java实例演示<转载备用>

    Thrift入门及Java实例演示 作者: Michael 日期: 年 月 日 •概述 •下载配置 •基本概念 .数据类型 .服务端编码基本步骤 .客户端编码基本步骤 .数据传输协议 •实例演示(ja ...

  3. SSH和三层架构的MVC模式的对应关系

    1.MVC(Model-View-Controller)设计模式: 首先让我们了解下MVC(Model-View-Controller)的概念: MVC全名是Model View Controller ...

  4. iOS:使用MVC模式帮ViewController瘦身

    如何给UIViewController瘦身 随着程序逻辑复杂度的提高,你是否也发现了App中一些ViewController的代码行数急剧增多,达到了2,3千行,甚至更多.这时如果想再添加一点功能或者 ...

  5. 三层架构和MVC模式

    目录[-] 1.三层架构 2.MVC 2.1 标准的MVC(Model-View-Controller) 2.2 Web MVC 3.三层架构和MVC的区别与联系 1.三层架构 三层架构(3-tier ...

  6. 【架构】MVC模式

    架构模式 如何设计一个程序的结构,这是一门专门的学问,叫做"架构模式"(architectural pattern),属于编程的方法论. MVC模式就是架构模式的一种,它不仅适用于 ...

  7. Android入门:MVC模式(中)

    MVC 模式的最基本概念是分层设计,把我们的代码基于 View(视图).Model(模型).Controller(控制器)进行分类封装,这样做的目的是为了清晰结构,使代码更易维护和扩展. 在上一篇文章 ...

  8. 关于三层架构和MVC模式的思考

    MVC模式 核心: 1.解耦Model和View,即使得Model可以被不同的展示,比如一批统计数据可以分别用柱状图.饼图表示 2.Controller用来保证Model和View的同步 Model ...

  9. Thrift入门及Java实例演示

    目录: 概述 下载配置 基本概念 数据类型 服务端编码基本步骤 客户端编码基本步骤 数据传输协议 实例演示(java) thrift生成代码 实现接口Iface TSimpleServer服务模型 T ...

随机推荐

  1. Vue学习(1)---Vue介绍

    Vue是什么 官方定义:Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库只关注视图层 ...

  2. 1025 PAT Ranking (25 分)

    Programming Ability Test (PAT) is organized by the College of Computer Science and Technology of Zhe ...

  3. 分享一下,PHP实现第四方QQ微信扫码登陆,不接入qq互联以及微信开发者平台就可以实现用户对接鹅厂,phpQQ微信扫码登陆

    自己抓的QQ包以及整合了网上一些已经封装好了的代码具体如下:QQ: <?php class QQ extends Curl_Api { //获取登录验证码 public function QRc ...

  4. Mysql数据库错误代码大全

                                                    Mysql数据库错误代码大全 出现较多的一些网页代码提示的意思: 1016错误:文件无法打开,使用后台修 ...

  5. mybatis源码配置文件解析之一:解析properties标签

    mybatis作为日常开发的常用ORM框架,在开发中起着很重要的作用,了解其源码对日常的开发有很大的帮助.源码版本为:3-3.4.x,可执行到github进行下载. 从这篇文章开始逐一分析mybati ...

  6. Centos7.x & RedHat7.x系统忘记 root 密码解决办法

    重启系统进入引导页面 先将机器重启 根据提示按下e进入内核编辑页面 找到linux16参数行,并在行尾加上rd.break,之后按下Ctrl+X重启 如上图所示,重启之后将进入救援模式. 这是依次输入 ...

  7. 05-CSV文件读取(问题)

    1.支持.txt,.log,.json三种格式 并且也支持.csv格式文件类型----.csv在使用时会出现乱码情况 2.当时遇到问题: CSV文件.txt文本内有数据,如:user ,pwd adm ...

  8. Java学习成长第一集

    由于最近所在项目组的项目临近结尾,所以有时间对自己近来的学习做个总结.不得不说,程序员不学习就退步这句话是真的很让人信服!自己入行将近一年的时间,所学的就是Java开发的专业,很羞愧的是现在的自己能力 ...

  9. Web三维编程入门总结之三:3D碰撞检测初探

    自己动手写一个方法比分析他人的写的方法困难很多,由此而来的对程序的进一步理解也是分析别人的代码很难得到的. 一.先来几张效果图: 1.场景中有两个半径为1的球体,蓝色线段从球心出发指向球体的“正向” ...

  10. 如何使用python在短时间内寻找完数

    完数:完全数(Perfect number),又称完美数或完备数,是一些特殊的自然数.它所有的真因子(即除了自身以外的约数)的和(即因子函数),恰好等于它本身.如果一个数恰好等于它的因子之和,则称该数 ...