框架的使用和解析iOSiOS开发

MVVM与RAC(吹水篇)

2016-02-19  本文已影响2891人  01_Jack

前言

首先MVC没什么不好,MVVM也没多么伟大,如果你愿意,可以把MVVM理解为特殊的MVC,就像等边三角形、直角三角形等与普通的三角形关系一样。MVVM是由MVC演变而来的,我们可以在MVC的基础上创建属于自己的开发模式。Massive View Controller?未必!controller作为viewmodel的管理者做好自己的事情就行(Single responsibility principle

创建viewmodel很简单,在controller初始化的时候就可以做,关于复杂界面的UI完全可以抽象出一个view在内部完成然后再添加到controller。监听viewmodel事件的代码怎么写?抽成一个方法/属性,在controller中调用/赋值就可以了,这样一来controller中也就那么几句代码。如viewmodel网络层存储层服务层的配合,datasourcedelegatecontroller中分离,使用protocolcategoryaspect等。当然,分离、封装这种事儿过犹不及,要把握好度。最终的目的都是DRY(Don't repeat yourself ),做好自己的事、内聚、解耦、复用。然而鱼和熊掌不可兼得,怎样取舍视具体项目而定。无论MVCS还是MVVM或者MVPVIPER等等都是由MVC演变过来的,只是招式,都是为了处理model-view-controller之间的关系,不必生搬硬套,更重要的是如何处理复杂场景的业务逻辑,如何更好的DRY


MVVM

先说说胖瘦model。胖model中不单有数据也有处理数据的方法,瘦model中只有数据。我们常用的MVC中的model通常都是瘦model(实际上更像MVCS),而MVVM是基于胖model构建的。MVVM只有VMV自然是viewview controllerM就是胖model胖model又可拆分成modelview model。与其说MVVM,倒不如说MVCVM更为贴切。这样一来,MVVM不过是把MVCC的交互逻辑拿出来放到VM中去,从而达到Lighter View Controllers的目的。按照MVCVM来理解,MVVMcontroller中数据处理的业务逻辑转移到view model确实可以让controller更专注的做自己,但是当业务逻辑很复杂同样会使view model中凝聚大量代码,此时view model也需要做进一步的细化。

MVVM弱化controller,强调view modelviewbindcontroller中的业务逻辑尽量转移到view model中,除开管理view的生命周期不谈,controller只是起到协调view modelview的作用。view model的主要职责是处理业务逻辑并给view提供数据,view model不关心view从而解耦也方便做单元测试。另外,view model中不能有view但是可以有其他view model
用链状结构表示大概长这样:

View/ViewController -> ViewModel ->Model

OK,controller持有viewview modelview model处理业务逻辑并提供model,那么如何将model中的数据展示到view上?这就是前面提到的将view modelview进行绑定,这个操作是在controller中完成的,也就是controller的职责之一,协调view modelview

整个流程还是挺眼熟的,跟MVC很相似。

view modelview绑定说白了就是让view model告诉view,现在数据变了,你该显示不同的内容。但是view model不可以持有view,因此赋值操作无法写在view model里而是在controller中完成。其实可选的方法很多,blockdelegatenotificationKVOtarget-action/invocation...

如果非要说优雅这个词,不谈RAC似乎显得太low,但是就像前面所说,可供选择的方法很多,不是非RAC不可。

RAC

说起RAC不得不说Stream这个概念,stream翻译过来是的意思,用来组词我们最先想到可能是水流电流,会有动态的画面感,流动即驱动,驱动的原动力是事件流RAC就是基于stream这个概念发展来的,即所谓的函数响应式编程( Functional Reactive Programming:FRP )

RACStream有两个子类:RACSignalRACSequenceSignal表示信号Sequence表示序列,两者都可以很好的描述所拥有的特性。

信号可发送、可接收、可合并、可压缩、可增删改查、可...。这些特性流同样拥有,信号即流,万物皆流,拥有流即可改造万物。

诗云:江河入海流序列可理解为大海,海纳百川序列可以汇集,形成一个更庞大的,也可以分离出所汇集的每一个

signal存在的意义就是加工并传递数据,所以signal需要一个订阅者subscriber来接收数据。有subscribersignal称为热信号,没有subscribersignal称为冷信号,没有意义。

举个栗子:

我是栗子🌰

很方便吧,然而并没有什么...用!这是怎么做到的?刚才不是还说需要subscriber吗,为啥没见到?贴一下源码:

@implementation RACSignal
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
 NSCParameterAssert(nextBlock != NULL);
 
 RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
 return [self subscribe:o];
}

@implementation RACSubscriber
+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
 RACSubscriber *subscriber = [[self alloc] init];

 subscriber->_next = [next copy];
 subscriber->_error = [error copy];
 subscriber->_completed = [completed copy];

 return subscriber;
}

@implementation UITextField (RACSignalSupport)
- (RACSignal *)rac_textSignal {
 @weakify(self);
 return [[[[[RACSignal
  defer:^{
   @strongify(self);
   return [RACSignal return:self];
  }]
  concat:[self rac_signalForControlEvents:UIControlEventAllEditingEvents]]
  map:^(UITextField *x) {
   return x.text;
  }]
  takeUntil:self.rac_willDeallocSignal]
  setNameWithFormat:@"%@ -rac_textSignal", RACDescription(self)];
}

可见,这个方法内部已经配置了订阅者,所以可以直接工作。订阅者内部拷贝nexterrorcompleted这三个block,其中errorcompletedNULLnext是基于textField的UIControlEventAllEditingEvents事件触发的,block内参数为textField.text。至此,从心理上起码可以接受RAC这种东西。

凡事要学会抓主要矛盾,切勿眉毛胡子一把抓。先别管代码里种种看不明白的天书,来看看这个方法做了什么,为啥可以监听UIControlEventAllEditingEvents事件

[self rac_signalForControlEvents:UIControlEventAllEditingEvents]

@implementation UIControl (RACSignalSupport)

- (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents {
 @weakify(self);

 return [[RACSignal
  createSignal:^(id<RACSubscriber> subscriber) {
   @strongify(self);

   [self addTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
   [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
    [subscriber sendCompleted];
   }]];

   return [RACDisposable disposableWithBlock:^{
    @strongify(self);
    [self removeTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
   }];
  }]
  setNameWithFormat:@"%@ -rac_signalForControlEvents: %lx", RACDescription(self), (unsigned long)controlEvents];
}

看到了啥?UIControl的分类以及target-actiontargetRACSubscriber的实例,action则为RACSubscriber的协议方法- (void)sendNext:(id)value。Let's go on!

@implementation RACSubscriber
- (void)sendNext:(id)value {
 @synchronized (self) {
  void (^nextBlock)(id) = [self.next copy];
  if (nextBlock == nil) return;

  nextBlock(value);
 }
}

拷贝self.next后执行nextBlock这个block。那么self.next又是啥?就是例子开始中的block

[_textField.rac_textSignal subscribeNext:^(id x) {
  NSLog(@"%@", x);
}];

再来理一遍思路看看RAC是如何一句代码完成对textFieldUIControlEventAllEditingEvents事件监听的。

1 将textField信号化并通过block将target-action转移到订阅者
2 创建订阅者并在订阅者内部拷贝外部subscribeNext中的block
3 当触发UIControlEventAllEditingEvents事件通过订阅者回调外部block


如果你已经理解这些,我们继续吧。回到这个方法,来看看天书们是否安好

@implementation UITextField (RACSignalSupport)

- (RACSignal *)rac_textSignal {
 @weakify(self);
 return [[[[[RACSignal
  defer:^{
   @strongify(self);
   return [RACSignal return:self];
  }]
  concat:[self rac_signalForControlEvents:UIControlEventAllEditingEvents]]
  map:^(UITextField *x) {
   return x.text;
  }]
  takeUntil:self.rac_willDeallocSignal]
  setNameWithFormat:@"%@ -rac_textSignal", RACDescription(self)];
}
@interface RACSignal (Operations)
// 创建一个信号,当有订阅者订阅信号,将冷信号转换为热信号
+ (RACSignal *)defer:(RACSignal * (^)(void))block;

@interface RACSignal (RACStream)
// 将信号传递给value
+ (RACSignal *)return:(id)value;
// 衔接新旧信号,旧信号执行完执行新信号
- (RACSignal *)concat:(RACSignal *)signal;

@interface RACStream (Operations)
// 信号映射
- (instancetype)map:(id (^)(id value))block;
// 当`signalTrigger`执行`next`或`completed`时,返回信号执行`completed`
- (RACSignal *)takeUntil:(RACSignal *)signalTrigger;

什么乱七八糟的。。!先别想这么多,试着结合上文把刚刚看到的注释拼接起来带入- (RACSignal *)rac_textSignal中可以得到这么一句话:

创建一个可变为热信号的冷信号,并将这个信号传递给textField,当信号传递完毕,利用target-action让订阅者监听UIControlEventAllEditingEvents事件,映射当前信号并作将textField.text做为block参数返回,这一系列操作直到textField.rac_willDeallocSignal执行next或者completed时结束。

那么这句代码的意思已经很清楚了

[_textField.rac_textSignal subscribeNext:^(id x) {
   NSLog(@"%@", x);
}];

_textField.rac_textSignal完成事件监听并管理信号的生命周期,subscribeNext创建订阅者订阅_textField.rac_textSignal监听事件。

似乎有点偏离主题,关于更多RAC知识,下篇文章接着说。

上一篇下一篇

猜你喜欢

热点阅读