iOS 架构模式

作者: 攻城虎 | 来源:发表于2019-02-26 15:30 被阅读59次

    MVC-面向对象

    image

    以上三者的交互关系。什么关系?

    1. Model 不与 View直接通话,如图中Model和View之间的两黄实线。
    2. Model 与 Controller 通话,如图中Model和Controller中两灰色虚实线、当中的绿色箭头、Model上橙色的KVO。
    3. View与 controller 通话,如图中Controller和View之间两灰色虚实线、当中的Outlet绿色箭头、黄色的delegate和data source。
    4. View上action 与 controller 上的target。
    5. Model上KVO发送端与controller上的黄色接受端。

    MVC实际使用

    目前App 中最常用的code习惯如下图

    image

    通过这张图可以发现, 用户信息页面作为业务场景Scene需要展示多种数据M(Blog/Draft/UserInfo), 所以对应的有多个View(blogTableView/draftTableView/image…), 但是, 每个MV之间并没有一个连接层C, 本来应该分散到各个C层处理的逻辑全部被打包丢到了Scene这一个地方处理, 也就是M-C-V变成了MM…-Scene-…VV, C层就这样莫名其妙的消失了.

    另外, 作为V的两个cell直接耦合了M(blog/draft), 这意味着这两个V的输入被绑死到了相应的M上, 复用无从谈起.

    MVC正常使用

    那么正确的MVC是什么呢?如下图

    image

    MVC 的缺点

    1、胖model的产生
    2、Massive View Controller的产生
    3、过度隔离V&M
    4、产生太多轻量级的model
    5、遗失网络逻辑
    6、较差的可测试性
    7、业务逻辑与视图逻辑强耦合

    相关参考连接

    https://www.jianshu.com/p/7e5fc5fb7ba3
    https://www.cnblogs.com/AnnieBabygn/p/7872552.html

    MVP-面向接口

    image

    Model View Presenter


    5.png

    Controller中的所有逻辑都放在Presenter中实现,与MVC不同的是Model与View是没有任何联系的,都是通过Presenter来交互

    Presenter持有MVPView与MVPModel

    #import "MVPViewController.h"
    #import "Presenter.h"
    #import "MVPModel.h"
    #import "MVPView.h"
    @interface MVPViewController () 
    @property (nonatomic, strong) Presenter *presenter;
    @property (nonatomic, strong) MVPView  *mvpView;
    @property (nonatomic, strong) MVPModel *mvpModel;
    @end
    @implementation MVPViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        _presenter = [[Presenter alloc] init];
        _mvpView = [[MVPView alloc] init];
        _mvpView.frame = self.view.bounds;
        [self.view addSubview:_mvpView];
        _mvpModel = [MVPModel new];
        _mvpModel.content = @"MVP的模式";
        // model还没赋值
        _presenter.model = _mvpModel;
        _presenter.view = _mvpView;
        [_presenter usageLogic];
    }
    @end
    
    

    MVVM-响应式编程

    MVVM其实是MVC的升级版,controller & view 直接通过viewModel 读取数据然后展示在界面上

    MVVM概念

    7.png

    MVVM 简单一点说 : 双向绑定(通过viewModel 绑定view 与 model)

    使用的严格限制

    1、View 引用了viewModel ,但是反过来不行
    2、viewModel引用了model,但是反过来不行
    3、MVVM模式依赖于数据绑定,它是一个框架级别的特性,用于自动连接对象属性和UI控件。

    viewModel 主要通过以下两种方式通知View更新UI

    1、Block
    2、使用reactiveCocoa库

    这个库包含了函数式编程和响应式编程。其实reactiveCocoa库就是MVVM模式的核心。
    不使用 RAC :ViewModel处理完数据需要刷新 View等操作都是通过 Block回调来实现,不免是有些麻烦的。
    使用 RAC :在 View初始化的时候就和 ViewModel进行绑定,只要 ViewModel的数据有所变化,便自动更新 View。
    RAC 缺点:数据绑定使得一个位置的 Bug被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了

    ReactiveCocoa

    reactiveCocoa 俗称RAC,属于函数式响应编程,极大的简化了逻辑,方便使用

    1、替换代理方法
    2、替换KVO
    3、替换NSNotification
    4、替换UIControl 的一些响应事件
    5、替换NSTimer
    等等,采用管道式通信机制,使逻辑更易理解

    ReactiveCocoa入门教程:第一部分
    ReactiveCocoa入门教程: 第二部分
    ReactiveCocoa源码解析
    ReactiveCocoa常用方法
    MVVM+RAC 从框架到实战

    RAC使用

    使用RAC模拟请求客户档案与仓库档案

    1.先请求客户档案数量,再根据数量分批次请求客户档案数据

    2.先请求仓库档案数量,再根据数量分批次请求仓库档案数据

    - (void)viewDidLoad
    
      RACSignal *consumerSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {    
    
            //模拟发送一次获取客户更新数量的请求
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
                //假如获取到count 5
                [self requestConsumerDataWith:subscriber];
            });
    
            return nil;
    
        }];
    
        [consumerSignal subscribeNext:^(id  _Nullable x) {
    
    g
           NSLog(@"客户档案更新结束%@",x);
        }];
    
        RACSignal *warehouseSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    
            //模拟发送一次获取仓库更新数量的请求
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
                //假如获取到count 5
                [self requestConsumerDataWith:subscriber];
            });
    
            return nil;
    
        }];
    
        [warehouseSignal subscribeNext:^(id  _Nullable x) {
    
            NSLog(@"仓库档案更新结束%@",x);
        }];
    
        //连接两个信号,只有两个信号都触发时,才能刷新UI 或者让HUD 消失
        [self rac_liftSelector:@selector(updateUIWithR1:r3:) withSignalsFromArray:@[warehouseSignal,consumerSignal]];  
    
    }
    
    - (void)requestConsumerDataWith:(id<RACSubscriber>)subScriber_consumer{
    
        RACSignal *consumerSignal = nil;
    
        for (int i = 1; i < 5; i++) {
    
            RACSignal *single_temp = [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
                    [subscriber sendNext:@(i)];
    
                });
    
                return nil;
    
            }]delay:i];
    
            if (!consumerSignal) {
                consumerSignal = single_temp;
            }else{
                consumerSignal = [consumerSignal combineLatestWith:single_temp];
            }
        }
    
        [consumerSignal subscribeNext:^(id  _Nullable x) {
            [subScriber_consumer sendNext:@"客户档案请求成功!!!"];
        }];
    }
    
    - (void)requestWarehouseDataWith:(id<RACSubscriber>)subScriber_warehouse{
    
        RACSignal *consumerSignal = nil;
    
        for (int i = 1; i < 5; i++) {
    
            RACSignal *single_temp = [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
                    [subscriber sendNext:@(i)];
    
                });
    
                return nil;
    
            }]delay:i];
    
            if (!consumerSignal) {
    
                consumerSignal = single_temp;
    
            }else{
    
                consumerSignal = [consumerSignal combineLatestWith:single_temp];
    
            }
        }
    
        [consumerSignal subscribeNext:^(id  _Nullable x) {
              [subScriber_warehouse sendNext:@"仓库档案请求成功!!!"];
        }];
    }
    
    // 更新UI
    
    - (void)updateUIWithR1:(id)data r3:(id)data2{
      
    

    相关文章

      网友评论

        本文标题:iOS 架构模式

        本文链接:https://www.haomeiwen.com/subject/mzbayqtx.html