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

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

内容简介: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模式实例演示》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

网页设计创意书(卷2)

网页设计创意书(卷2)

麦克尼尔 / 图灵编辑部 / 人民邮电 / 2012-1 / 49.00元

《网页设计创意书(卷2)》是《网页设计创意书》的卷2,但并非其简单补充,而是作者基于近几年网站发展新趋势的再创作。《网页设计创意书(卷2)》先讲解了如何从他人的优秀设计中寻找灵感,接着阐述了重点、对比、平衡、对齐等网站设计的基本原则,然后将网站按类型、设计元素、风格和主题、结构样式和结构元素分类,并分章介绍了每一类的设计技巧。《网页设计创意书(卷2)》语言简练,结合作者精挑细选的网站实例,通俗易懂......一起来看看 《网页设计创意书(卷2)》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具