澳门永利m5900cc

澳门永利m5900cc

  • 鑱旂郴鎴戜滑鈥斿崐鏈堣皥缃 2019-07-16
  • 【江州天气】江州天气预报,一周、15天、30天江州天气预报查询 2019-07-16
  • 雄安新区:个人产权住房以共有产权房为主 2019-07-16
  • 吉林省委书记巴音朝鲁致信网友:共同构筑网上网下最大同心圆,集聚振兴发展最强正能量 2019-07-16
  • 1943年2月17日:赖传珠关于敌向盐阜区扫荡情况的报告 2019-07-16
  • 揪心悲剧!男孩身体伸出天窗 撞限高栏不幸身亡 2019-07-16
  • 台湾医师:电子烟危害多 2019-07-16
  • 【抚宁天气】抚宁天气预报,一周、15天、30天抚宁天气预报查询 2019-07-16
  • 《中国古典和谐政治理念与治国方略研究》简介 2019-07-16
  • 江苏通报少女被泼开水烫伤案:行凶者未实施开水灌喉 2019-07-16
  • 从未雨绸缪到枕戈待旦 2019-07-16
  • 2018反转新闻盘点:看清真相有多难? 2019-07-16
  • 2019女足世界杯:耐克助力中国女足全新球衣亮相巴黎 2019-07-16
  • 高洪波:当图书管理员“监守自盗” 和战友偷书看 2019-07-16
  • 2018年国家社科基金后期资助项目立项名单公布 2019-07-16
  • 登录 立即注册
    金钱:

    澳门永利m5900cc

    iOS架构入门 - MVC模式实例演示 [复制链接]

    2018-8-10 10:35
    EmailLi 阅读:665 评论:1 赞:0
    Tag:  

    澳门永利m5900cc www.alfanarrealestate.com

    image.png

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

    • 控制器(Controller)--> 负责转发请求,对请求进行处理。

    • 视图(View) --> 界面设计人员进行图形界面设计。

    • 模型(Model) --> 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。

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

    • Model层: 数据处理层,包括网络请求,数据加工

    • View层: 所有App上看得到的界面

    • Controller层: Model 与 View层的中介,把Model数据在View上展示出来

    • 目的: 低耦合,可复用

    image

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @implementation DemoViewController
     
    - (void)viewDidLoad {
        [super viewDidLoad];
         
        //setupUI
         
        //1.createView
        UIView *view = [[UIView alloc]init];
        view.frame = CGRectMake(100100100100);
        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,里面有一张图片,图片依赖于网络资源,这样写的好处,可以直接让 View 在 Controller 中就能拿到资源,不需要传值

    缺点!!:

    • 导致Controller特别臃肿,里面代码特别多,视图一复杂起来,代码量可能过1000行,不好维护

    • 写在Controller里无法复用,除非你在 VC2里面 copy 当前VC中的 View的代码

    • 特别low!!会被懂架构的人瞧不起,喷你根本不是MVC,是MC架构,可能还要你来段喊麦证明一下自己(-。-)

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

    1. 先给自己洗脑,iOS的Controller不是UIViewController,而是普通的Controller,没有View。(很关键的一步)

    2. 模块化划分,每个模块对应自己的一个View,例如Demo2模块,View层里面有个Demo2View,将界面元素写到View中

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //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:控件点击事件如何回调给控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    //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层按钮的点击事件回调~
    }

    image

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @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

    image

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

    1. 不用洗脑,给自己一个大耳刮子让自己清醒清醒,这iOS的Controller就算是UIViewController,也没看到M啊,没有Model。(很关键的一步)

    2. 模块化划分,每个模块对应自己的一个Model,例如Demo2模块,View层里面有个Demo2Model,将网络请求&&数据处理写到Model中

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    //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 配合 继承,进阶提高效率

    • 常用的方法,抽一个基类出来,继承是子类可以拥有父类的方法,重新父类的方法即可,无需声明

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //数据基类
    @interface MNBaseDatas : NSObject
     
    //请求数据成功
    typedef void (^MNsuccessBlock)(NSArray *array);
     
    + (void)fetchDatasSuccessBlock:(MNsuccessBlock)block;
     
    + (void)fetchDatasSuccessBlock:(MNsuccessBlock)block
                      failureBlock:(MNfailureBlock)failure;

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @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];
        }];
    }

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    //基类控制器
    @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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //继承自`MNBaseViewController`
    /*数据结构是 - @[],没有section的tableVIew*/
    @interface MNBaseControllerTypeNoSection : MNBaseViewController
     
    @end
     
    /*数据结构是 - @[@[]],有section的tableVIew*/
    @interface MNBaseControllerTypeHadSection : MNBaseViewController
     
    @end
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //实现
    /**没有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之类的方法等,大幅度减少冗余代码~

    demo示例0.png

    demo示例1.png

    demo示例2.png

    demo示例3.png

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

    总结

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

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

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

    Demo:https://github.com/miniLV/MVC-Demo

    分享到:
    我来说两句
    facelist
    您需要登录后才可以评论 登录 | 立即注册
    所有评论(1)
    key丶蜗牛 2018-8-13 14:40
    挺好的,适合写代码乱的人和乱用MVC的人
    回复
    关闭

    每日头条

    通过邮件订阅最新 澳门永利m5900cc 信息
    上一条 /4 下一条
    联系我们
    关闭
    合作电话:
    13802416937
    Email:
    435399051@qq.com
    商务市场合作/投稿
    问题反馈及帮助
    联系我们

    广告投放| Github|申请友链|手机版|澳门永利m5900cc ( 澳门永利m5900cc )

    返回顶部