iOS架构入门 - MVC模式实例演示

栏目: IOS · 发布时间: 6年前

内容简介:MVC模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。除此之外,此模式通过对复杂度的简化,使程序结构更加直观以上出自先看这张图,这张图是iOS的
iOS架构入门 - MVC模式实例演示

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

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

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

  • Model层: 数据处理层,包括网络请求,数据加工
  • View层: 所有App上看得到的界面
  • Controller层: Model 与 View层的中介,把Model数据在View上展示出来
  • 目的: 低耦合,可复用
iOS架构入门 - MVC模式实例演示

先看这张图,这张图是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模式实例演示
接下来看这张 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
    iOS架构入门 - MVC模式实例演示

如何告别 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 之类的方法等,大幅度减少冗余代码~

iOS架构入门 - MVC模式实例演示
iOS架构入门 - MVC模式实例演示
iOS架构入门 - MVC模式实例演示
iOS架构入门 - MVC模式实例演示

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

总结

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

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

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

MVC架构实战-Demo

欢迎star~


以上所述就是小编给大家介绍的《iOS架构入门 - MVC模式实例演示》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Python Machine Learning

Python Machine Learning

Sebastian Raschka / Packt Publishing - ebooks Account / 2015-9 / USD 44.99

About This Book Leverage Python' s most powerful open-source libraries for deep learning, data wrangling, and data visualization Learn effective strategies and best practices to improve and opti......一起来看看 《Python Machine Learning》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具