参考资料
https://segmentfault.com/a/1190000003894677
https://www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1
FRP(Functional Reactive Programming) 函数响应式编程
理解:
a = 5; b = a + 5;// b = 10; 然后 a变成了6,那么如果不执行任何命令,b依然是10; 如果想让b变成11,让b和a产生一定的约束关系,就是响应式编程。 另外还有excel的公式。MVVM
- MVVM中的M,只用它来存放元数据。
- ViewModel要考虑模块化,便于在不同的ViewController中复用。目前的理解:ViewModel可以和ViewController有多对多的关系。
- ViewModel是为View服务的,它的命名和字段定义应该根据View的需要来进行。
ReactiveCocoa
缺点:
- 学习成本,一个新人阅读代码会有难度。
优点:
- 可以省去很多变量,不需要使用实例变量来追踪瞬时状态
思维的转变
一个场景:
一个页面API请求假如有5个,然后,有的是并行,有的是串行。如图:API1、2、3是串行关系,1、4、5是并行关系。现在要求网络请求开始时,显示菊花,所有网络请求结束后,让菊花消失。
如果不使用rac响应式编程,我们原来的做法可能是设置3个变量:a1、a2、a3,分别表示第一行、第二行、第三行网络请求是否结束。在开始时请求时a1、a2、a3都置为YES,在每行结束时将所有变量设置为NO。并且判断是否其他变量也为NO。如果都为NO,则让菊花消失。也就是判断所有变量为NO,让菊花消失的逻辑,要出现多次。如果使用RAC,思想将转变为:每个请求,对应一个变量。a1、a2、a3、a4、a5。然后有一个变量a的值和a1、a2、a3、a4、a5进行绑定,也就是只要当a1、a2、a3、a4、a5都为YES的时候,a为YES,然后对于a也会绑定一个事件:为NO时让菊花小时、为YES时让菊花显示。就清晰了很多。
最佳实践
什么时候绑定(取舍):
MVVM和ReactiveCocoa其实是解决两个阶段的问题。虽然配合起来使用很好,但是也不一定什么场景都配合使用,也就是说有的时候,其实只是需要抽离出ViewModel层,但是不一定非得数据绑定。例如下面场景:一个VC包含一个UITableView,数据源是一个对象数组(m_Array),m_Array可能会有以下变化:
- 指针发生改变:例如刷新后指向新请求的网络数据解析而成的数组。
- 增加一个元素
- 减少一个元素
- 某一个元素的某一个属性发生改变。 这就会遇到以下几个问题:
每次改变都必须通过KVO能够检测到的方式来写。例如
NSMutableArray *fromKVC = [self mutableArrayValueForKey:@"postedImagesIds"];
[fromKVC addObject:imagePosted.imagePostedId]; [fromKVC replaceObjectAtIndex:i withObject:imagePosted.imagePostedId];
试想我们将数组中的某个元素model1传递给了下一个VC,在这个VC中我们对model1中的那么属性进行了修改
model1.name = @"new value";
但是为了能让绑定检测到,我们需要这么写:
Model *newModel = [[Model alloc] init];newModel.xxx = model1.xxx; // 其他没有改变的属性....newModel.name = @"new value"; [fromKVC replaceObjectAtIndex:i withObject:imagePosted.imagePostedId];
也就是我们相守了绑定带来的方便,缺引入了代码上的麻烦与不干净。甚至如果不了解这套机制的人很容易不知道要这么写。
然后再想想,绑定虽然带来了方便,任何时候绑定的数据源变了,就执行相应的代码(比如刷新UITableView),我们虽然不用手动考虑什么时候执行刷新UITableView,但是也失去了主动控制的权利,假如场景中太频繁的会改变数据源,我们知道刷新UITableView也是要消耗资源的,所以本来我们可以以lazy的方式,在需要刷新的时候刷新,但是现在失去了这个主动权利了,所以要权衡。
还有一点,不使用绑定,我可以对于某一行的变化告诉UITableView之刷新这一行,如果使用绑定,往往就是有任何改变就全量刷新了。列表数据很多时也不得不考虑这件事(通过增加额外的逻辑似乎也能办到,但是还是不直观)所以,结论就是,要看具体情形而定,不一定每次ViewModel都要通过绑定来通知变化。
目前我的做法是:- 数据源是model list的,并且数据一般很多,频繁变化,一般都是应用的首页,最需要考虑体验好,内存消耗小的时候,不使用绑定。
- 数据源是简单的model,或者不是最最主要,数据最多的的最核心的那部分列表,可以使用绑定,好维护。
遗留问题
- 内存泄漏
- 合并信号的写法
- 不使用绑定的时候通过什么回调(目前采用delegate)。
- 有时候数据变化不想出发绑定里定义的事件
- 封装变化,让viewmodel更加抽象(基类)。
- ViewModel之间的依赖,什么场景发送消息,如何发送消息,如何实现一个消息总线。