ReactiveCocoa理解笔记(2)

MVVM和ReactiveCocoa

在MVVM中UIViewController讲专注的负责View的管理、界面流程跳转,它不会直接持有、处理数据。UIViewController需要准备数据来组织、展现View,这时它需要通过对应的View-Model来准备,数据的获取、处理、持有都由View-Model来负责。估计这时大多数人心里有有了一些想法:View-Model在处理准备好数据以后,完全可以通过代理、block来实现通知UIViewController刷新界面。这个想法也是可以的,不使用ReactiveCocoa也可以实现MVVM开发模式,但是我还是愿意花时间来学习、了解ReactiveCocoa。初始接触ReactiveCocoa时,感觉它是一个很怪异的东东,以前“正”着写的,现在需要“反”着写,语法怪异,理解困难。其他朋友们有必要花时间学习吗?

目前有一个很火的理念:数据与视图绑定。就是当数据变化时,视图不需要额外的处理,便可正确地呈现最新的数据。而这也是ReactiveCocoa最重要的亮点。RAC与MVVM编程模式结合,可以方便的处理界面变化,减少View-ModelUIViewController连接,让我们两层的核心功能。

ReactiveCocoa不是单一功能的模块,它是一个Framework,提供了一整套解决方案。其核心思想是「响应数据的变化」,在这个基础上有了Signal的概念,进而可以帮助减少状态类型,易于使用MVVM架构,方便的响应式编程等等。

为什么要使用ReactiveCocoa

  1. 开发过程中,状态以及状态之间依赖过多,RAC更加有效率地处理事件流,而无需显式去管理状态。在过程式编程中,状态变化是最难跟踪,最头痛的事。这个是我使用RAC最重要的一点。
  • 减少方法的调用,由于它跟踪状态和值的变化,因此不需要状态更新时手动调用,减少出错的可能。
  • 提供统一的消息传递机制,将OC中的通知、代理,KVO以及其它所有UIControl事件的变化都进行监控,当变化发生时,就会传递事件和值。
  • 当值随着事件变换时,可以使用combineLatest、map、filter等函数便利地对值进行变换操作。

比如:我们想要实现一个需求:当“count”变量中的字符串改变后即时做出相应的反馈。用KVO我们一般会这样做

`
// In your class viewDidLoad/init
[self addObserver:self
forKeyPath:@"count"
options:NSKeyValueObservingOptionNew
context:nil];

// In dealloc
[self removeObserver:self
forKeyPath:@"count"
context:nil];

// rewrite in your class

  • (void)observeValueForKeyPath:(NSString)keyPath
    ofObject:(id)object
    change:(NSDictionary
    )change
    context:(void *)context
    {
    if ([keyPath isEqualToString:@"count"]) {
    //do ....
    }
    }
    `

如果我们在工程中使用ReactiveCocoa后,只有短短几行,还无需重写方法

[RACObserve(self, count) subscribeNext:^(NSNumber *count) { //do .... }];

看到RAC的实现是不是觉得很简单,代码简洁清晰,下面了解一下RAC

RACSignal

RAC为应用中发生的不同事件流提供了一个标准接口。在ReactiveCocoa术语中这个叫做信号,由RACSignal类表示。RAC的核心就是RACSignal发送事件流给它的订阅者,共有三种类型的事件:nexterrorcompleted。一个signal可以在completed前发送任意数量的next事件;也会因为error终止。简单的说就是errorcompleted只能发送一次,在completed前可以发送多次next事件。

ReactiveCocoa理解笔记(2)_第1张图片
信号订阅

信号是一个发送一连串信息的载体体. 如果它还没有订阅者,那么它就是一个冷信号,现在不会起任何作用。只有信号被订阅了,它才会向它的订阅者发送信息,这时信号处于激活状态,称之为热信号

冷信号默认什么也不干,所以我们需要避免在开发过程中创建一堆冷信号,比如下面这段代码,如果不被订阅,或者不被连接,那么它没有任何意义。

RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { NSLog(@"signal"); [subscriber sendNext:@"1"]; [subscriber sendNext:@"2"]; [subscriber sendCompleted]; return nil; }];

我们已经创建了一个冷信号signal,但因为没有被subscribe或连接,所以什么也不会发生。
加了下面这段代码后,signal就处于激活状态了,block里的代码就会被执行。

订阅示例:

[signal subscribeNext:^(NSString *name):^{ NSLog(@"name = %@", name); }];

连接示例:

RAC(self.usernameField, text) = RACObserve(self, name);

RACSignal有很多方法可以来订阅不同的事件类型。每个方法都需要至少一个block,当事件发生时就会执行block中的逻辑。比如每次next事件发生时,subscribeNext:方法提供的block都会执行。

  • (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock;

  • (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock completed:(void (^)(void))completedBlock;

  • (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock;

  • (RACDisposable *)subscribeError:(void (^)(NSError *error))errorBlock;

  • (RACDisposable *)subscribeCompleted:(void (^)(void))completedBlock;

  • (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock;

  • (RACDisposable *)subscribeError:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock;

ReactiveCocoa框架使用category来为很多基本UIKit控件添加signal。这样你就能给控件添加订阅了,如UITextField添加了rac_textSignal

创建RACSignal

使用

+(RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe

方法创建信号,在block需要实现“RACSubscriber”协议,最后还需返回RACDisposable,用于处理、销毁信号。

`
RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id subscriber) {
NSLog(@"signal");
[subscriber sendNext:@"1"];
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
return nil;
}];

[signal subscribeNext:^(NSString *name):^{
NSLog(@"name = %@", name);
}];
`

使用“RACObserve”宏定义快捷创建信号

[RACObserve(self, name) subscribeNext:^(NSString *name) { NSLog(@"count %@", name); }];

这些是一些简单的用法,下面给一个实用的,在开发过程中可以实用的示例,这是一个根据URL获取数据的RACSignal创建示例:

`

pragma mark - Get请求

  • (RACSignal *)fetchGetServerFromURL:(NSURL *)url
    {

    GLogString(url.description);
    return [RACSignal createSignal:^RACDisposable *(id subscriber) {
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

      //申明请求的数据是json类型
      manager.requestSerializer = [AFJSONRequestSerializer serializer];
      manager.requestSerializer.timeoutInterval = 30.0f;
      
      //申明返回的结果是json类型
      manager.responseSerializer = [AFJSONResponseSerializer serializer];
      
      //如果报接受类型不一致请替换一致text/html或别的
      manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html"];
      
      AFHTTPRequestOperation *operation = [manager GET:url.absoluteString
                                            parameters:nil
                                               success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                  GLogString(responseObject);
                                  MBAResponse *response = [MBAResponse responseWithJsonDict:responseObject];
                                  if (response.requestSuccess) {
                                      [subscriber sendNext:response.dataDict];
                                      [subscriber sendCompleted];
                                  }
                                  else {
                                      GLogString(response.error.localizedDescription);
                                      [subscriber sendError:response.error];
                                  }
                                  
                              } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                  [subscriber sendError:error];
                              }];
      
      return [RACDisposable disposableWithBlock:^{
          //信号销毁时,取消请求
          [operation cancel];
      }];
    

    }];
    }
    `

信号创建以后,订阅者可以获取数据

`
//创建获取数据的信号
RACSignal *signal = [self fetchGetServerFromURL:[NSURL URLWithString:@"http://www.sina.com"]];

//订阅信号,编写信号数据流处理
RACDisposable *disposable = [signal subscribeNext:^(id x) {
NSLog(@"data= %@", x);
} error:^(NSError *error) {
NSLog(@"error = %@", error);
}];
`

在请求的过程中,我们如果不需要了,也可以取消请求

`
//创建获取数据的信号
RACSignal *signal = [self fetchGetServerFromURL:[NSURL URLWithString:@"http://www.sina.com"]];

//订阅信号,编写信号数据流处理
RACDisposable *disposable = [signal subscribeNext:^(id x) {
NSLog(@"data= %@", x);
} error:^(NSError *error) {
NSLog(@"error = %@", error);
}];

//取消请求
if (!disposable.isDisposed) {
[disposable dispose];
}
`

当我们手动销毁信号,这时之前创建信号时生成的RACDisposable对象就会生效

return [RACDisposable disposableWithBlock:^{ //信号销毁时,取消请求 [operation cancel]; }];

这一连串代码示例就是信号创建-订阅-处理(或取消)的过程,只要先理解清楚信号的工作原理,在后面的学习、应用中,会发现RAC更加便捷,更加简单。

总结

这张主要说了RAC的优点以及基本用法,这些基本用法能应对开发过程中各种复杂的需求吗,下面会说说RAC的一下高级用法。

你可能感兴趣的:(ReactiveCocoa理解笔记(2))