zoukankan      html  css  js  c++  java
  • 如何让RAC框架融入到工程中的MVVM模式(MVVM中View和VM职责划分)

    参考:https://msdn.microsoft.com/en-us/library/hh848246.aspx

    在MVVM中要实现数据数据源的处理与控件逻辑分离,控件数据与VM中的数据源做一个映射关系,其实想一想可能会觉得是一件简单的事情,但是实际上着手做又是另外一码事情了

    那么如何做呢

    由于MVVM模式中VM充当了adapter(适配器)的作用。那么要使用这种模式我们第一步应该做的事情是业务划分,而不是想当然就往上 这是MVVM,那又是MVVM。

    那么如何业务拆分呢?

    首先我们进入VM的结构里去看:

    1、VM中一般都包含数据源也就是一个Model,一个数据源是某个控件或者是视图的存储体现。

    2、VM中会有多个数据处理和请求的方法(这也是VM的主要工作,将重用逻辑分装到VM这个游离的类中,从而减轻了VC的大量工作,同时也将各个VC中相同VM处理的部分给抽象了出来,对于相同接口,或者是数据源来说,相同的处理大大降低)

    问题1:VM如何跟一个控件进行绑定?

    由于VM是一个携带数据源的adapter,VM本身可能就是可变化的一个东西,VM中的数据源(model)也会跟着VM更新替换或者接口请求而变化,那么我们做这种数据绑定的操作通过基于VM,或者是Model的key进行绑定貌似没有那么合适,那么怎么办,我们只能寻找不变的地方->那便是View中的keypath,由于我们可以将VM作为一个属性传给一个View,而我们知道属性都是有keypath的,如果属性中某个值发生了变化我们只要对view中这个属性的相应值相对于view的keypath做一个绑定就可以了,也就是说view到VM和VM到view的双向绑定可以在view中通过view的keypath来完成。

    问题2:在哪里进行双向绑定的操作?

    双向绑定是对VM和View的数据进行同步的操作,最开始我认为绑定操作应该在setVM的方法当中,因为VM会变化,所以有这个想法,后来发现由于VM会变化会不会有当VM没有被释放而造成一种单方向的绑定而这个绑定实际上没有任何作用。而且每次做setVM方法操作的时候都会有VM重新绑定的操作。当然由于这种方法是基于VM的keypath进行操作的,所以当我们换成通过View的keypath来进行双向绑定的操作似乎这个问题就被规避了,由于View对象要是最终的双向绑定的holder,当View消失了,由于监听的对象一直是View这个东西,我们也不用去过多的考虑是否要让VM去dispose资源等问题。所以总结了刚才这些考虑,我认为双向绑定的操作应该放在View的init方法当中并且是通过View的keypath来进行绑定比较合适

    问题3:数据校验?

    VM还有一个操作时数据校验,由于一般情况我们需要对控件输入源的数据进行校验,那么我们应该有一个数据校验的过程来提供输入源是否合法,这个校验的过程应该在哪里?由于我们在问题2中已经说明了双向绑定其实可以在init方法当中完成,而这个过程中其实还可以做一个操作那便是对数据源的flatmapper操作,通过flatmapper操作之后我们能够让控件或者是View作出相应的处理和UI响应,为什么我没有把这个校验放在ViewModel当中呢?1、首先校验的过程是一个绑定前检查的过程,不同的View有不同的校验方式放在VM当中会显得VM中的处理会有冗余的方法。2、放在View中处理便于做UI处理(比如弹窗,或者改变页面中提示文字的颜色,控件使能等)

    问题4:数据处理

    数据处理相对于校验和双向绑定的过程来说更加简单一点。由于这时候我们不需要做什么UI响应或者是控件使能,而且我们处理完后能够直接更改到VM中的属性进而改变视图中的控件的展示。所以这个处理过程不用太过纠结。放在VM中处理就好了。

    于是综上得到一个结果:


    View:

    -(instancetype)init{

    initialize processor progress...

    View and VM TWO way binding.....
    (
    RACChannelTerminal *channelTerminal = RACChannelTo(self,control.propertyKeypath)或者self.control.property_rac_newValueSignal;
    RACChannelTerminal *channelTerminal2 = RACChannelTo(self, viewModel.valueKeyPath);

    //@interface RACChannelTerminal : RACSignal <RACSubscriber>
    [channelTerminal subscribe:channelTerminal2];
    [channelTerminal2 subscribe:channelTerminal];

    [[RACObserve(self, viewModel.valueKeyPath) flattenMapper] subscribeNext:^(id x) {
    apply to view for update the control|更新视图显示内容
    NSLog(@"x = %@", x);
    }];
    [RACObserve(self, control.propertyKeyPath) subscribeNext:^(id x) {
    notice viewModel to synchronize the value | 通知VM更新model操作
    NSLog(@"x = %@", x);
    }];
    )
    validate during the dingding processor progress.
    }


    VM的操作:

    -(void)dataProcessorMethodForDisplaying{

    处理数据转换为视图或者是控件显示需要设置的属性值。


    }

    - (void)dataProccessMethodForNetworking{

    处理数据使之成为网络请求接口中所需的数据
    }

    (先盗一张图)

    然后我们要做的处理是:

     

    这样之后页面和ViewModel,ViewModel就能够进行数据同步了,由于这种绑定操作是基于KVO方式实现的,我们根本就不用担心如何刷新页面,甚至像TableView这样复杂的视图在高度不发生变化的情况下都可以不掉用reloadData函数就能够进行数据刷新的操作。

    如当这两个双向绑定弄好后,我直接请求数据,数据拿到了就有了Model,ViewModel中的Model被set了一个新的值,此时KVO机制能够在model的值apply到ViewModel的属性时对ViewModel进行同步更新,而这个同步更新的操作又会引起连锁反应,触发View和ViewModel的双向绑定的KVO这时候造成的结果就是数据同步到了界面显示。

    同理反过来也成立,操作视图时页面的数据更新到了ViewModel中的属性当中,ViewModel中的数据再同步到model当中。

    双向绑定常用到的方法

    RAC(target,keypath)

    RACChannel(target,keypath)

    RACTerminal(target,keypaht)

    这三个宏能够处理大部分绑定的事件

  • 相关阅读:
    洛谷P2569 (BZOJ1855)[SCOI2010]股票交易 【单调队列优化DP】
    洛谷 P2254 [NOI2005]瑰丽华尔兹(单调栈优化DP)
    CF372C Watching Fireworks is Fun(单调队列优化DP)
    2019牛客全国多校第八场A题 All-one Matrices(单调栈)
    HDU3896 Greatest TC(双联通分量+倍增)
    2019牛客多校第7场
    ZOJ 2112 Dynamic Rankings(树状数组+主席树)
    2019 杭电多校第六场 题解
    HDU2242 考研路茫茫——空调教室 (双联通分+树形DP)
    HDU5536 Chip Factory
  • 原文地址:https://www.cnblogs.com/codetime/p/6422763.html
Copyright © 2011-2022 走看看