加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ReactiveCocoa理解(持续更新中)

发布时间:2020-12-15 04:54:02 所属栏目:百科 来源:网络整理
导读:引子 ReactiveCocoa 是 OC 的一个强大的框架。它的强大之处不仅仅在于提供了很多用于简化工作的方法,更在于它提供了一种思考方式。比如这样的场景:一个登录界面,有用户名文本框、密码框以及登录按钮。只有用户名文本框文本长度大于等于6并且密码框文本长

引子

ReactiveCocoa 是 OC 的一个强大的框架。它的强大之处不仅仅在于提供了很多用于简化工作的方法,更在于它提供了一种思考方式。比如这样的场景:一个登录界面,有用户名文本框、密码框以及登录按钮。只有用户名文本框文本长度大于等于6并且密码框文本长度大于等于6时,登录按钮才能被点击。按照普遍的实现方式是:每当文本框或密码框文本发生变化时,都检查登录按钮此时是否可被点击。这种方法将精力集中在通过各种途径来满足当前的需求。 而 ReactiveCocoa 中的解决方案是:登录按钮的可被点击=文本框文本长度>=6 并且 密码框文本长度>=6。这种方法则将精力集中在问题本身,想的是怎么把这个需求描述清楚,这跟咱们人本身的思维方式保持一致,也就更易理解了。下面会有更详细的描述。


信号

ReactiveCocoa(RAC)是一个函数式反应编程(Functional reactive programming)的 Objective-C 框架。啥是函数式反应编程?理解了这个东东才能更好地使用 RAC。

当前的编程世界中,编辑语言最多是:面向过程(如 C)、面向对象(如 C++/Java),这两者不是现在的主题,在此不做界面。响应式编程与以上两种不同,它是面向信号流的。信号流,由一系列信号组成,而信号则包含一些对象,是信息的载体。概念不多说,举例可就明白多了。

比如咱们现在有这样的任务:站在路边观察过来的车辆。当一辆车子过来时,我就产生一个信号,这个信号里包含了一辆车子的信息。当一辆辆车子过来,那么一个个信号也就产生了,这一系列信号就组成了信号流。哈哈,是不是很简单?理解了的话,恭喜你,你已经掌握了核心部分啦。不过,这也太简单了点,简单到这东东有啥用呢?先别急,咱们继续(我废话好多-_-!)。

我做的事情是观察过来的车辆,就叫车辆观察员吧。现在又来一人,他的任务是观察过来车辆时的时候点,记录每辆车过来的时间,就叫他时间记录员。这个时间记录员完全没必要再去观察过来的车辆,因为有我这车辆观察员在呢,只需在车子来时,我告诉他一声,来车子了就行,然后他就查看此时的时间,再记录下来。这时,时间记录员就产生了一个新的信号,这个信号包含一个时间信息。注意,这个时间信号产自于我的车辆信号,即车辆信号映射(map)出一个时间信号。

又来一人,此人是飞机观察员,他的任务是观察天上飞过的飞机。当一架飞机飞过来时,就产生了一个信号,这个信号里包含这架飞机的信息。

再一人,任务是当有飞机飞过时,就写下“airplane”;当车辆过来时,就写下“car”。当有飞机飞过或车辆过来时,就产生了一个信号,信号里的信息是一个字符串。这个字符串信号产自于我的车辆信号,以及飞机观察员的飞机信号,也就是将两个信号给组合(combine)成一个字符串信号。

映射和组合是信号操作的最基础的方式。我们可以看到,我的观察车辆信号可以被多次使用,车辆信号只要我产生一次,大家就都能使用了,他们就没有必要再去观察车辆了,只要关注自己的任务就行,避免了重复劳动。这说明信号是可以被多次使用的,映射或组合操作是产生新的信号,且并不会影响原来的信号。

通过映射或组合可以衍生出多种操作,比如过滤(filter)。比如一个奔驰观察员,当我的车辆信号发生时,我把这个信息告诉奔驰观察员,然后此人就根据这时的车辆的牌子来判断,如果是奔驰车,则生成车辆信号;如果不是奔驰,则啥也不做。这时,奔驰观察员产生的也是车辆信号,而且所有的车辆都是奔驰车。我产生的车辆则是所有牌子的车。这就是过滤,这实际上是通过映射产生的新信号(不是我这个车辆观察员的每个信号都产生一个新信号,而是只针对奔驰车这一特定信息来产生新信号。其它的信号,则被忽略了)。


信号状态

在 RAC 中用RACSignal来代表信号流。signal有三种状态:正常状态、完成状态、错误状态。

  • 正常状态:正在等待下一信号的来临。(next
  • 完成状态:任务完成了,不会再继续产生信号。比如我这车辆观察员下班后,任务已完成,即使再有车辆过来,我也会置之不理。(complete
  • 错误状态:生成了错误,不会再继续产生信号。比如我突然生病,得去医院看病了,当然也就不会再继续观察过来车辆了。(error

信号订阅

信号有三种状态,如果我们对信号的某种感兴趣,就可以对其订阅:“嗨我对你的下一信号感有兴趣,如果发生了,记得通知我一下”,而我们要做的就是只要定义当新的信号产生时,要执行的 block 即可:

  [signal subscribeNext:^(id value){
    /*
     *  这里就是对 signal的的 Next 订阅,每当其有信号产生时,这个 block 就会被调用
     *  这个 block 的参数:value 就是信号所携带的信息数据。
     */
  }];

比如对我的观察的车辆信号订阅:

  RACSignal *signalCar = 生成观察车辆的信号
  [signalCar subscribeNext:^(id car) {
    NSLog(“%@“,car); // 每当signalCar 有新的信号产生时,这里就会打印 car 的信息。
  }];

与信号的三种状态相对应,有三个订阅方法:

  - subscribeNext:(void (^)(id x))nextBlock;
  - subscribeCompleted:(void (^)(void))completedBlock;
  - subscribeError:(void (^)(NSError *error))errorBlock;

这里刻意忽略掉了返回值,暂时你也不需要了解它,这并不影响你的使用。


RAC 提供的几个常用方法

本文的目的在于引领入门,所以这里只会介绍几个常用的方法,让你能稍微感受一下其魅力。RAC 的方法基本以 rac 开头,接下来你就会看到RAC 对 UIKit 里提供了很多很方便的支持,来看看:

a)UIAlertView+RACSignalSupport

RAC为 UIAlertView 提供了- (RACSignal *)rac_buttonClickedSignal;方法,此方法是为警告框的按钮点击事件创建了一个signal。

用法如下:

  UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:@"rac demo" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定",nil];
  [[alertView rac_buttonClickedSignal] subscribeNext:^(id x) {
    if ([x integerValue] == 1) {
      NSLog(@"点击了确定");
    } else {
      NSLog(@"点击了取消");
    }
  }];
  [alertView show];

是不是很爽?妈妈再也不用担心我写各种烦人的 delegate 了!RAC也为UIActionSheet类提供了该方法。


b)UITextField (RACSignalSupport)

RAC为 UITextFiled 提供了- (RACSignal *)rac_textSignal;方法。这个方法为文本框创建了一个 signal,每当这个文本框的文本改变时,这个 signal 就会 send next。

比如:

  [textField rac_textSignal] subscribeNext:^(NSString *newText) {
    NSLog(@“%@”,newText);
  }];
RAC也为 UITextView 提供了该方法。


c)NSNotificationCenter (RACSupport)

RAC 为NSNotificationCenter提供了方法:- (RACSignal *)rac_addObserverForName:(NSString *)notificationName object:(id)object;这个 signal在每次相应通知发出时,会 send这个通知相关的NSNotification。比如:

  [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification *keyboardWillShowNotifcation) {
    NSLog(@"键盘展现通知");
  }];

啊,终于再也不用为各个通知取名字了!


d) 观察者支持

RAC 提供了一个简单的观察者宏定义:

RACObserve(TARGET,KEYPATH)

这个宏定义创建了一个 signal,这个 signal 会在 TARGET的 KEYPATH 发生改变时,send next。比如:

  RACSignal *signalPersonName = RACObserve(self,person.name);
  @weakify(self);
  [signalPersonName subscribeNext:^(NSString *newName) {
    @strongify(self);
    self.lblName.text = newName;
  }];

对,Observes在 RAC 的世界里就是这么简单,甚至连removeObserver...都不需要。

尼玛呀,原来Observes还可以写得这么简单,抓狂吧!终于可以远离烦人的addObserver和removeObserver了。

上面的的@weakify(self)以及@strongify(self)是为了避免在 block 中的循环引用 self 的问题。

__weak __typeof__(self) __weak_self = self; // @weakify(self)的实际内容  
__strong __typeof__(self) self = __weak_self; // @strongify(self)的实际内容

具体可问度娘、google。

e)RAC宏

这是最让我佩服 RAC 的地方了!这是一个神奇的宏。

RAC(TARGET,…) 它有两种方式:

- RAC(TARGET,KEYPATH,NILVALUE)
- RAC(TARGET,KEYPATH)

这个宏将一个信号流与一个对象的属性绑在一起,当这个 signal 有新的信号时即 next,这个 next 中的对象value将会被自动赋值到 target 的 keypath 中。第一种方法则带有 nilValue,这是在 next 中的对象value为 nil时,会被赋值给 target 的 keypath 的值。

例:

RAC(self,objectProperty) = objectSignal;
RAC(self,stringProperty,@"foobar") = stringSignal;
RAC(self,integerProperty,@42) = integerSignal;

是的,单从这里,还看不到有什么神奇的地方,但是当 RAC 与 MVVM 相结合的时候,全得依靠它,这简直是绝配。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读