iOS架构入门 - MVC模式实例演示
MVC模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。除此之外,此模式通过对复杂度的简化,使程序结构更加直观
- 控制器(Controller)--> 负责转发请求,对请求进行处理。
- 视图(View) --> 界面设计人员进行图形界面设计。
- 模型(Model) --> 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。
以上出自维基百科
资料,下面说点人为描述(简单易懂的)~
- Model层: 数据处理层,包括网络请求,数据加工
- View层: 所有App上看得到的界面
- Controller层: Model 与 View层的中介,把Model数据在View上展示出来
- 目的: 低耦合,可复用
先看这张图,这张图是iOS的MVC
架构中最经常出现的图了吧,因为IOS中的Controlller
是 UIViewController
,所以导致很多人会把视图
写在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
的时候也这样写过,先说这样写的好处,以及初学者为什么会这么写:
- 比如按钮,可以在当前控制器直接
add target:
添加点击事件,在当前控制器内就能调用到点击方法,不需要设置代理之类的; - 比如要找某个界面,直接切到这个界面对应的
controller
就行,因为View
写在Controller
里面,不用去别的地方找,就这里有; - 比如一个View,里面有一张图片,图片依赖于网络资源,这样写的好处,可以直接让
View
在Controller
中就能拿到资源,不需要传值
缺点!!:
- 导致
Controller
特别臃肿,里面代码特别多,视图一复杂起来,代码量可能过1000行,不好维护 - 写在
Controller
里无法复用,除非你在 VC2里面 copy 当前VC中的View
的代码 - 特别low!!会被懂架构的人瞧不起,喷你根本不是
MVC
,是MC
架构,可能还要你来段喊麦证明一下自己(-。-)
如何告别MC
模式,真正走到MVC
?
- 先给自己洗脑,
iOS
的Controller
不是UIViewController
,而是普通的Controller
,没有View
。(很关键的一步) - 模块化划分,每个模块对应自己的一个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
的时候又这样写过,先说这样写的好处,以及初学者为什么会这么写:
- 简单,网络请求完,直接在当前控制器刷新
TableView
的数据源 - 比如要找某个界面的网络请求,直接切到这个界面对应的
controller
就行,因为数据请求 写在Controller
里面,不用去别的地方找,就这里有; - 比如当前网络请求接口,需要外部参数,比如前一个界面的
uuid
,这样写的好处,可以直接让当前请求在Controller
中就能拿到资源,不需要传值
缺点!!:
- 又导致
Controller
特别臃肿,里面代码特别多,如果当前控制器需要多次请求,代码量可能过1000行,不好维护 - 写在
Controller
里无法复用,除非你在 VC2里面 copy 当前VC中的网络请求
的代码 - 如果某些接口有依赖要求,接口1请求完再请求接口2,嵌套起来,辣眼睛的程度差点治好我多年的近视
- 特别low!!会被懂架构的人瞧不起,喷你根本不是
MVC
,如果你还用了上面的View
写在Controller
的操作的话,恭喜你,最终大法 -Controller架构
顺利完成,并不需要什么Model
&&View
如何告别
VC
模式,真正走到MVC
?
- 不用洗脑,给自己一个大耳刮子让自己清醒清醒,这
iOS
的Controller
就算是UIViewController
,也没看到M
啊,没有Model
。(很关键的一步) - 模块化划分,每个模块对应自己的一个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
成为tableView
的delegate
和dataSource
,所有的子类都无需再声明 - 如果有需要用到
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
代码也不会多。
欢迎star~
iOS架构入门 - MVC模式实例演示的更多相关文章
- (转)浅析三层架构与MVC模式的区别
MVC模式介绍: MVC全名是Model ViewController,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用于组织代码用一种业务逻辑和数据 ...
- Thrift入门及Java实例演示<转载备用>
Thrift入门及Java实例演示 作者: Michael 日期: 年 月 日 •概述 •下载配置 •基本概念 .数据类型 .服务端编码基本步骤 .客户端编码基本步骤 .数据传输协议 •实例演示(ja ...
- SSH和三层架构的MVC模式的对应关系
1.MVC(Model-View-Controller)设计模式: 首先让我们了解下MVC(Model-View-Controller)的概念: MVC全名是Model View Controller ...
- iOS:使用MVC模式帮ViewController瘦身
如何给UIViewController瘦身 随着程序逻辑复杂度的提高,你是否也发现了App中一些ViewController的代码行数急剧增多,达到了2,3千行,甚至更多.这时如果想再添加一点功能或者 ...
- 三层架构和MVC模式
目录[-] 1.三层架构 2.MVC 2.1 标准的MVC(Model-View-Controller) 2.2 Web MVC 3.三层架构和MVC的区别与联系 1.三层架构 三层架构(3-tier ...
- 【架构】MVC模式
架构模式 如何设计一个程序的结构,这是一门专门的学问,叫做"架构模式"(architectural pattern),属于编程的方法论. MVC模式就是架构模式的一种,它不仅适用于 ...
- Android入门:MVC模式(中)
MVC 模式的最基本概念是分层设计,把我们的代码基于 View(视图).Model(模型).Controller(控制器)进行分类封装,这样做的目的是为了清晰结构,使代码更易维护和扩展. 在上一篇文章 ...
- 关于三层架构和MVC模式的思考
MVC模式 核心: 1.解耦Model和View,即使得Model可以被不同的展示,比如一批统计数据可以分别用柱状图.饼图表示 2.Controller用来保证Model和View的同步 Model ...
- Thrift入门及Java实例演示
目录: 概述 下载配置 基本概念 数据类型 服务端编码基本步骤 客户端编码基本步骤 数据传输协议 实例演示(java) thrift生成代码 实现接口Iface TSimpleServer服务模型 T ...
随机推荐
- FastJson反序列化和构造函数之间的一点小秘密
各位看官大家下午好,FastJson想必大家都很熟悉了,很常见的Json序列化工具.今天在下要和大家分享一波FastJson反序列化和构造函数之间的一点小秘密. 下面先进入大家都深恶痛绝的做题环节.哈 ...
- Java代理笔记
代理顾名思义,就是一个中间层,当我们要使用某个方法时,不直接调用,而是告诉代理,让代理替我们去请求方法,并返回结果.在这个过程中,我们只知道代理执行并返回给了我们操作结果,至于它有没有其他操作并不知道 ...
- .NET Core 3.1 的REST 和gRPC 性能测试
看到越南小哥 的github 上的Evaluating Performance of REST vs. gRPC , 使用的是.NET Core 3.0 , 今天我把它升级到.NET Core 3.1 ...
- 【网络安全】——客户端安全(浏览器安全、XSS、CSRF、Clickjacking)
近在学习网络安全相关的知识,于是先从业内一本系统讲Web安全的书<白帽子讲Web安全>系统学习Web安全的相关知识.在此整理书中的知识层次,不求详尽,只求自己对整个Web安全梗概有所了 ...
- 让我来教你如何免费使用RHEL小红帽系统
RHEL安装注册过程中遇到的问题 从开始注册到正常使用 如何获取正版RHEL 注意事项 VMware虚拟机下载安装 安装中出现的问题 从开始注册到正常使用 答主是个动手能力比较强的人 ,所以当老师讲到 ...
- 在.net core中完美解决多租户分库分表的问题
前几天有人想做一个多租户的平台,每个租户一个库,可以进行水平扩展,应用端根据登录信息,切换到不同的租户库 计划用ef core实现,他们说做不出来,需要动态创建dbContext,不好实现 然而这个使 ...
- Java第三十四天,IO操作(续集),非基本对象的读写——序列化流
一.序列化与反序列化 以前在对文件的操作过程当中,读写的对象都是最基本的数据类型,即非引用数据类型.那么如果我们对饮用数据类型(即对象类型)数据进行读写时,应该如何做呢?这就用到了序列化与反序列化. ...
- C/C++内存详解
众所周知,堆和栈是数据结构中的两种数据结构类型,堆是一种具有优先顺序的完全二叉树(或者说是一种优先队列,因为它在一定的优先顺序下满足队列先进先出的特点),排队打饭就是它的典型实例,栈是一种后进先出的数 ...
- .NET Core 3 WPF MVVM框架 Prism系列之导航系统
本文将介绍如何在.NET Core3环境下使用MVVM框架Prism基于区域Region的导航系统 在讲解Prism导航系统之前,我们先来看看一个例子,我在之前的demo项目创建一个登录界面: 我们看 ...
- 【python实现卷积神经网络】损失函数的定义(均方误差损失、交叉熵损失)
代码来源:https://github.com/eriklindernoren/ML-From-Scratch 卷积神经网络中卷积层Conv2D(带stride.padding)的具体实现:https ...