博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
RAC(ReactiveCocoa)学习之道
阅读量:5078 次
发布时间:2019-06-12

本文共 20550 字,大约阅读时间需要 68 分钟。

1、ReactiveCocoa简介

  ReactiveCocoa(简称RAC),是由Github开源的一个应用于iOS和iOS开发的新框架。Cocoa是苹果整套框架的简称,因此很多苹果框架喜欢以Cocoa结尾。RAC具有函数式编程和响应式编程的特性,主要吸取了 .Net 的 Reactive Extensions 的设计和实现。

2、ReactiveCocoa作用

  在我们iOS开发过程中,经常会响应某些事件来处理某些业务逻辑,例如按钮的点击,上拉刷新,网络请求,属性的变化(通过KVO)或者用户位置的变化(通过CoreLocation)。但是这些事件都用不同的方式来处理,比如action、delegate、KVO、callback(回调)等。

  其实这些事情,都可以通过RAC处理,ReactiveCocoa为时间提供了很多处理方法,而且利用RAC处理事件很方便,可以把要处理的时间,和监听的时间的代码放在一起,这样非常方便我们管理,就不需要调到对应的方法里。非常符合我们开发中 高聚合,低耦合 的思想。

3、编程思想

  在开发中我们也不能太依赖于某个框架,否则这个框架不更新了,导致项目后期没法维护,比如之前Facebook提供的 three20框架,在当时也是神器,但是后来不更新了,也就没有什么人用了。因此学习一个框架,还是有必要了解它的 编程思想

  先简单介绍下目前已知的编程思想。

  3.1、面向过程:处理时间以过程为核心,一步一步的实现。

  3.2、面向对象:万物皆对象

  3.3、链式编程思想:是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码使代码可读性好。a(1).b(2).c(3)

    • 链式编程特点:方法的返回值必须是block,block必须有返回值(本身对象,或者可以叫方法调用者),block参数(需要操作的值或者内容)
    • 代表:masonry框架 
UIView *redView = [[UIView alloc] init];    redView.backgroundColor = [UIColor redColor];    [self.view addSubview:redView];        // mas_makeConstraints: 作用:给控件设置布局    // - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block    // 1、创建一个约束制造者    // 2、调用block(maker)    // 3、[constraintMaker install]: 遍历约束制造者的所有约束控件添加约束        // 参数: block    [redView mas_makeConstraints:^(MASConstraintMaker *make) {        // 描述控件的约束                // 上下左右间距都为10        // make.left -> NASViewConstraint        // make.left.top:把左边和顶部的约束全部保存到make.contrains                // equalTo:方法        // equalTo返回值: block        make.left.top.equalTo(@10);        make.right.bottom.equalTo(@(-10));    }];

    下面我们通过写一个简单的计算器来学习一下链式编程思想。

    首先,我们需要创建一个继承NSObject的类,命名为CalculatorMaker,然后在 .h 文件中声明一个计算结果的属性跟一个加法计算的方法,返回值为一个block。

@property (nonatomic, assign) int result;/** 相加 */- (CalculatorMaker *(^)(int num))add;

    然后我们需要在 .m 文件中把这个相加的方法实现。

- (CalculatorMaker * (^)(int num))add {    return ^(int num) {        _result += num;        return self;    };}

    这样,一个加法计算的方法就完成了,然后在ViewController中导入头文件,并在viewDidLoad中使用这个方法。

// 1、创建计算制造者    CalculatorMaker *maker = [[CalculatorMaker alloc] init];        // 链式编程思想:maker.add(10).add(20).add(30).add(40);    // 提供一个没有参数的add方法,返回值block        int result = [maker.add(10).add(20).add(30).add(40) result];    NSLog(@"result = %@", @(result));

    但是这样实现跟第三方Masonry实现的方法不太一样,所以接下来我们需要再写一个类,把计算器中的加、减、乘、除方法的调用整合到一起,用一个block就能实现这些功能。首先创建一个分类,继承NSObject,命名为Caculator,因为这个方法是继承自NSObject的,所以我们要先导入头文件 #import "CalculatorMaker.h", 然后在 .h 中我们先声明一个方法,以后计算都可以直接受用这个方法,一调用这个方法就返回结果。模仿着Masonry的实现方法,我们自己写一个方法。

// 以后计算都使用这个方法,一调用这个方法就返回结果+ (int)makeCalculator:(void (^)(CalculatorMaker *))block;

    方法声明后,肯定是要实现 TA 的。下面便是实现 TA 方法的代码。返回值为 int 类型的,因为你要返回的是一个结果。

+ (int)makeCalculator:(void (^)(CalculatorMaker *))block {    // 创建计算制造者    CalculatorMaker *maker = [[CalculatorMaker alloc] init];    // 计算    block(maker);    return maker.result;}

    有了这个方法,那么计算器的使用就会更加的简便。下面就让我们来看看怎么实现 TA。回到 viewController.m 文件中,导入头文件 #import "NSObject+Calculator.h",然后我们就可以实现高聚合的block方法了。

int result = [NSObject makeCaculator:^(CalculatorMaker *maker) {    // 把所有的计算代码封装到这里    maker.add(10).add(20);    maker.add(30).add(40);}];NSLog(@"result = %@", @(result));

    上面的代码输出的结果跟之前的一样,但是这样使用要比之前的使用更好,跟Masonry一样,一个block方法实现所有的功能。

    好了,链式编程思想就讲到这里了,后面深入就需要自己研究了。 

    完整代码。

  3.4、响应式编程思想:不需要考虑调用顺序,只需要知道考虑结果,类似于蝴蝶效应,产生一个事件,会影响很多东西,这些事件像流一样的传播出去,然后影响结果,借用面向对象的一句话,万物皆是流。

    举个例子,如果你要求 c = a + b; 那么你必须得知道 a 跟 b 的值,先定义 a 跟 b,并给他们赋值,这是正常的思路。但是响应式编程的话,你可以先定义 a 跟 b 的值,然后让 c = a + b,在后面给 a 跟 b 赋值,把 a 跟 b 与 c 绑定了,赋值后 c 的值也会相应的改变。这就是响应式编程思想,经过上面一个小例子,相比大家应该能理解上面那句不需要考虑调用顺序,只需要考虑结果这句话的意思了吧。

    • 代表:KVO运用

   下面我们来看一下KVO的实现,首先创建一个工程,然后创建一个类(Person)继承自NSObject,然后在 .h 文件里面声明一个年龄(age)属性。

/** 年龄 */    @property (nonatomic, assign) NSInteger age;

    然后回到viewController.m 文件,导入头文件,声明一个Person,并在viewDidLoad创建 TA,给 TA 添加KVO,并把方法实现,重写touchesBegan:withEvent:

每次点击屏幕让Person类中的age加1。

- (void)viewDidLoad {    [super viewDidLoad];        _p = [[Person alloc] init];        // 添加观察者    [_p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];}/** *  监听的属性只要一次改变就调用 */- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary
*)change context:(void *)context { NSLog(@"%@", @(_p.age));}- (void)touchesBegan:(NSSet
*)touches withEvent:(UIEvent *)event { _p.age++;}

    写完之后,如果你没有写错的话,那么你运行完之后点击空白的地方,能在控制台看到age每输出一次就增加一次。但是如果你把 _p.age++ 改成 _p -> _age++,然后在Person类中添加如下代码,再次运行,点击空白处,你就会发现控制台不会再输出age了。

@interface Person : NSObject{    @public    NSInteger _age;}

    从上面的例子你就能发现,KVO的底层实现就是去判断有没有调用一个对象的set方法。如果你想更底层的研究,就需要使用runtime来拦截方法,这里就不介绍了,有个demo的,想要了解的看下。

  3.5、函数式编程思想:是把操作尽量携程一系列嵌套的函数或者方法调用。

    • 函数式编程特点:每个方法必须有返回值(本身对象),把函数或者Block当做参数 block参数 (需要操作的值) block返回值 (操作结果)
    • 代表:ReactiveCocoa

      下面我们来写一个计算器并且有判断功能的小demo。首先,创建一个集成NSObject的类Calculator,因为既能计算,又能判断值是否相等,所以我们得先声明两个属性,一个是记录计算的结果,一个是判断两个数是否相等。

/** 记录结果 */@property (nonatomic, assign) NSInteger result;/** 判断是否相等 */@property (nonatomic, assign) BOOL isEqual;

      然后声明两个方法,一个是加法计算,一个是判断两个值是否相等。

/** *  加法 */- (instancetype)add:(NSInteger(^)(NSInteger result))block;/** *  判断两个值是否相等 */- (instancetype)equal:(BOOL(^)(NSInteger result))block;

     到 .m 文件中去实现这两个方法,因为函数值编程思想是把block或者函数作为参数,操作的结果作为返回值,所以,它的实现方法就是这样的。

- (instancetype)add:(NSInteger(^)(NSInteger result))block {    _result = block(_result);    return self;}- (instancetype)equal:(BOOL (^)(NSInteger))block {    _isEqual = block(_result);    return self;}

    既然方法都写完了,回到ViewController里面,我们来使用一下 TA 看看。下面是加法计算获得值的使用。

Calculator *calculator = [[Calculator alloc] init];        NSInteger result = [[calculator add:^(NSInteger result) {        result += 10;        result += 20;        result += 30;            return result;    }] result];    NSLog(@"%ld", result);

    然后我们加入判断的方法,再看看是如何使用这个的。

BOOL isEqual = [[[calculator add:^(NSInteger result) {        result += 10;        result += 20;        result += 30;                return result;    }] equal:^BOOL(NSInteger result) {        return result == 600;    }] isEqual];    NSLog(@"%d", isEqual);

 

4、ReactiveCocoa编程思想

    ReactiveCocoa结合了几种编程风格:

  •     函数式编程(Functional Programming)
  •     响应式编程(Reactive Programming)

    所有,ReactiveCocoa会被描述为函数响应式编程(FRP)框架。

    以后解决问题就不需要考虑调用顺序,直接考虑结果,把每一次操作都写成一系列嵌套的方法中,使代码高聚合,方便管理。

 

5、如何导入ReactiveCocoa框架

    通常会使用CocoaPods(管理第三方框架的插件)来导入ReactiveCocoa框架,当然,你也可以用终端来导入,看个人喜好。

    

    如果你直接按照上面这样导入的话,就会报下面的错误。

    所以你得在最上面加入   use_frameworks!  这句话,这样,才能完成ReactiveCocoa框架的导入。

 

6、ReactiveCocoa常见类()

    学习框架首要之处:得先搞清楚框架中常用的类,在RAC中最核心的类RACSiganl,搞定了这个类就能基本使用ReactiveCocoa开发了。

    RACSiganl:信号类,一般表示将来有数据传递,只要有数据改变,信号内部接收到数据,就会马上发出数据。

    •  信号类(RACSiganl):只是表示当数据改变时,信号内部会发出数据,它本身不具备发送信号的能力,而是交给内部一个订阅者去发出。
    • 默认一个信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发。
    • 如何订阅信号:调用信号RACSignal的subscribeNext就能订阅。
    • RACSiganl简单实用:
  • // RACSignal使用步骤: // 1、创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe // 2、 发送信号 - (void)sendNext:(id)value // 3、订阅信号,才会激活信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock // RACSignal底层实现: // 1、创建信号,首先把didSubscribe保存到信号中,还不会触发 // 2、当信号被订阅,也就是调用signal的subscribeNext:nextBlock // 2.1 subscribeNext内部会创建订阅者subscriber,并且把nextBlock保存到subscriber中 // 2.2 subscribeNext内部会调用siganl的didSubscribe // 3、siganl的didSubscribe中调用[subscriber sendNext:@1]; // 3.1 sendNext底层其实就是执行subscriber的nextBlock

 

    下面是创建的方法。

// 1.创建信号 createSignal:didSubscribe(block)    // RACDisposable:取消订阅    // RACSubscriber:发送数据        // createSignal方法:    // 1.创建RACDynamicSignal    // 2.把didSubscribe保存到RACDynamicSignal        RACSignal *siganl = [RACSignal createSignal:^RACDisposable *(id
subscriber) { // block调用时刻:当信号被订阅的时候就会调用 // block作用:描述当前信号哪些数据需要发送 // _subscriber = subscriber; // 发送数据 NSLog(@"调用了didSubscribe"); // 通常:传递数据出去 [subscriber sendNext:@1]; // 调用订阅者的nextBlock // 如果信号,想要被取消,就必须返回一个RACDisposable return [RACDisposable disposableWithBlock:^{ // 信号什么时候被取消:1.自动取消,当一个信号的订阅者被销毁的时候,就会自动取消订阅 2.主动取消 // block调用时刻:一旦一个信号,被取消订阅的时候就会调用 // block作用:当信号取消订阅,用于清空一些资源 NSLog(@"取消订阅"); }]; }]; // subscribeNext: // 1.创建订阅者 // 2.把nextBlock保存到订阅者里面 // 订阅信号 // 只要订阅信号,就会返回一个取消订阅信号的类 RACDisposable *disposable = [siganl subscribeNext:^(id x) { // block:只要信号内部发送数据,就会调用这个block NSLog(@"%@",x); }]; // 取消订阅 [disposable dispose];
  • RACSubscriber:表示订阅者的意思,用于发送信号,这是一个协议,不是一个雷,只要遵守这个协议,并且实现方法才能成为订阅者。通过create创建的信号,都有一个订阅者,帮助他发送数据。
  • RACDisposable:用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发 TA。
    • 使用场景:不想监听某个信号时,可以通过它主动取消订阅信号。
  • RACSubject:信号提供者,自己可以充当信号,又能发送信号。
    • 使用场景:通常用来代替代理,有了 TA,就不必要定义代理了。
  • RACReplaySubject:重复提供信号类,RACSubject的子类。
  • RACReplaySubject 与 RACSubject 区别:
    • RACReplaySubject可以先发送信号,在订阅信号,RACSubject就不可以。
    • 如果一个信号被订阅一次,就需要把之前的值重复发送一遍,使用重复提供信号类。
    • 可以设置capacity数量来限制缓存的value的数量,即只缓充最新的几个值。

 

  • RACSubject 和 RACReplaySubject简单使用:
  • // RACSubject使用步骤    // 1.创建信号 [RACSubject subject],跟RACSiganl不一样,创建信号时没有block。    // 2.订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock    // 3.发送信号 sendNext:(id)value    // RACSubject:底层实现和RACSignal不一样。    // 1.调用subscribeNext订阅信号,只是把订阅者保存起来,并且订阅者的nextBlock已经赋值了。    // 2.调用sendNext发送信号,遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock。    // 1.创建信号    RACSubject *subject = [RACSubject subject];    // 2.订阅信号    [subject subscribeNext:^(id x) {        // block调用时刻:当信号发出新值,就会调用.        NSLog(@"第一个订阅者%@",x);    }];    [subject subscribeNext:^(id x) {        // block调用时刻:当信号发出新值,就会调用.        NSLog(@"第二个订阅者%@",x);    }];    // 3.发送信号    [subject sendNext:@"1"];    // RACReplaySubject使用步骤:    // 1.创建信号 [RACSubject subject],跟RACSiganl不一样,创建信号时没有block。    // 2.可以先订阅信号,也可以先发送信号。    // 2.1 订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock    // 2.2 发送信号 sendNext:(id)value    // RACReplaySubject:底层实现和RACSubject不一样。    // 1.调用sendNext发送信号,把值保存起来,然后遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock。    // 2.调用subscribeNext订阅信号,遍历保存的所有值,一个一个调用订阅者的nextBlock    // 如果想当一个信号被订阅,就重复播放之前所有值,需要先发送信号,在订阅信号。    // 也就是先保存值,在订阅值。    // 1.创建信号    RACReplaySubject *replaySubject = [RACReplaySubject subject];    // 2.发送信号    [replaySubject sendNext:@1];    [replaySubject sendNext:@2];    // 3.订阅信号    [replaySubject subscribeNext:^(id x) {        NSLog(@"第一个订阅者接收到的数据%@",x);    }];    // 订阅信号    [replaySubject subscribeNext:^(id x) {        NSLog(@"第二个订阅者接收到的数据%@",x);    }];

     

  • RACSubject替换代理
    1. 给当前控制器添加一个按钮,modal到另一个控制器界面
    2. 另一个控制器view中有个按钮,点击按钮,通知当前控制器
  1. 创建一个控制器FirstViewController一个控制器SecondViewController都继承UIViewController,然后在 SecondViewController.h 里面添加一个RACSubject代替代理。当然,你得先导入头文件。#import "ReactiveCocoa.h"
@interface SecondViewController : UIViewController@property (nonatomic, strong) RACSubject *delegateSignal;@end

      2. 在 SecondViewController.m 中实现一个点击按钮的事件。

- (IBAction)secondButton:(id)sender {    // 通知第一个控制器,告诉它,按钮被点了        // 通知代理    // 判断代理信号是否有值    if (self.delegateSignal) {        // 有值,才需要通知        [self.delegateSignal sendNext:nil];    }}

  3. 在 FirstViewController.m 中也实现一个按钮的点击事件。

- (IBAction)firstButton:(id)sender {    // 创建第二个控制器    SecondViewController *secondVC = [[SecondViewController alloc] init];        // 设置代理信号    secondVC.delegateSignal = [RACSubject subject];        // 订阅代理信号    [secondVC.delegateSignal subscribeNext:^(id x) {        NSLog(@"点击了通知按钮");    }];        // 跳转到第二个控制器    [self presentViewController:secondVC animated:YES completion:nil];}

    这样,你就完成了使用RACSubject 替换代理的方法。

  • RACTuple:元祖类,类似于NSArray,用来包装值。
  • RACSequence:RAC中的集合类,用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典。
    • 使用场景:1、字典转模型
    • RACSequence和RACTuple简单使用。
// 1.遍历数组    NSArray *numbers = @[@1,@2,@3,@4];    // 这里其实是三步    // 第一步: 把数组转换成集合RACSequence numbers.rac_sequence    // 第二步: 把集合RACSequence转换RACSignal信号类,numbers.rac_sequence.signal    // 第三步: 订阅信号,激活信号,会自动把集合中的所有值,遍历出来。    [numbers.rac_sequence.signal subscribeNext:^(id x) {        NSLog(@"%@",x);    }];    // 2.遍历字典,遍历出来的键值对会包装成RACTuple(元组对象)    NSDictionary *dict = @{
@"name":@"xmg",@"age":@18}; [dict.rac_sequence.signal subscribeNext:^(RACTuple *x) { // 解包元组,会把元组的值,按顺序给参数里面的变量赋值 RACTupleUnpack(NSString *key,NSString *value) = x; // 相当于以下写法// NSString *key = x[0];// NSString *value = x[1]; NSLog(@"%@ %@",key,value); }]; // 3.字典转模型 // 3.1 OC写法 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil]; NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath]; NSMutableArray *items = [NSMutableArray array]; for (NSDictionary *dict in dictArr) { FlagItem *item = [FlagItem flagWithDict:dict]; [items addObject:item]; } // 3.2 RAC写法 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil]; NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath]; NSMutableArray *flags = [NSMutableArray array]; _flags = flags; // rac_sequence注意点:调用subscribeNext,并不会马上执行nextBlock,而是会等一会。 [dictArr.rac_sequence.signal subscribeNext:^(id x) { // 运用RAC遍历字典,x:字典 FlagItem *item = [FlagItem flagWithDict:x]; [flags addObject:item]; }]; NSLog(@"%@", NSStringFromCGRect([UIScreen mainScreen].bounds)); // 3.3 RAC高级写法: NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil]; NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath]; // map:映射的意思,目的:把原始值value映射成一个新值 // array: 把集合转换成数组 // 底层实现:当信号被订阅,会遍历集合中的原始值,映射成新值,并且保存到新的数组里。 NSArray *flags = [[dictArr.rac_sequence map:^id(id value) { return [FlagItem flagWithDict:value]; }] array];
  • RACCommand:RAC中用于处理事件的类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中,TA 可以很方便的监控事件的执行过程。
    • 使用场景:监听按钮点击,网络请求
    • RACCommand简单使用
// 一、RACCommand使用步骤:    // 1.创建命令 initWithSignalBlock:(RACSignal * (^)(id input))signalBlock    // 2.在signalBlock中,创建RACSignal,并且作为signalBlock的返回值    // 3.执行命令 - (RACSignal *)execute:(id)input    // 二、RACCommand使用注意:    // 1.signalBlock必须要返回一个信号,不能传nil.    // 2.如果不想要传递信号,直接创建空的信号[RACSignal empty];    // 3.RACCommand中信号如果数据传递完,必须调用[subscriber sendCompleted],这时命令才会执行完毕,否则永远处于执行中。    // 4.RACCommand需要被强引用,否则接收不到RACCommand中的信号,因此RACCommand中的信号是延迟发送的。    // 三、RACCommand设计思想:内部signalBlock为什么要返回一个信号,这个信号有什么用。    // 1.在RAC开发中,通常会把网络请求封装到RACCommand,直接执行某个RACCommand就能发送请求。    // 2.当RACCommand内部请求到数据的时候,需要把请求的数据传递给外界,这时候就需要通过signalBlock返回的信号传递了。    // 四、如何拿到RACCommand中返回信号发出的数据。    // 1.RACCommand有个执行信号源executionSignals,这个是signal of signals(信号的信号),意思是信号发出的数据是信号,不是普通的类型。    // 2.订阅executionSignals就能拿到RACCommand中返回的信号,然后订阅signalBlock返回的信号,就能获取发出的值。    // 五、监听当前命令是否正在执行executing    // 六、使用场景,监听按钮点击,网络请求    // 1.创建命令    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {        NSLog(@"执行命令");        // 创建空信号,必须返回信号        //        return [RACSignal empty];        // 2.创建信号,用来传递数据        return [RACSignal createSignal:^RACDisposable *(id
subscriber) { [subscriber sendNext:@"请求数据"]; // 注意:数据传递完,最好调用sendCompleted,这时命令才执行完毕。 [subscriber sendCompleted]; return nil; }]; }]; // 强引用命令,不要被销毁,否则接收不到数据 _conmmand = command; // 3.订阅RACCommand中的信号 [command.executionSignals subscribeNext:^(id x) { [x subscribeNext:^(id x) { NSLog(@"%@",x); }]; }]; // RAC高级用法 // switchToLatest:用于signal of signals,获取signal of signals发出的最新信号,也就是可以直接拿到RACCommand中的信号 [command.executionSignals.switchToLatest subscribeNext:^(id x) { NSLog(@"%@",x); }]; // 4.监听命令是否执行完毕,默认会来一次,可以直接跳过,skip表示跳过第一次信号。 [[command.executing skip:1] subscribeNext:^(id x) { if ([x boolValue] == YES) { // 正在执行 NSLog(@"正在执行"); }else{ // 执行完成 NSLog(@"执行完成"); } }]; // 5.执行命令 [self.conmmand execute:@1];
  • RACMulticastConnection:用于当一个信号,被多次订阅时,为了保证创建信号时,避免多次调用创建信号中的block,造成副作用,可以使用这个类处理。
    • RACMulticastConnection通过RACSignal的-publish或者-muticast:方法创建
    • RACMulticastConnection简单使用:
// RACMulticastConnection使用步骤:    // 1.创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id
subscriber))didSubscribe // 2.创建连接 RACMulticastConnection *connect = [signal publish]; // 3.订阅信号,注意:订阅的不在是之前的信号,而是连接的信号。 [connect.signal subscribeNext:nextBlock] // 4.连接 [connect connect] // RACMulticastConnection底层原理: // 1.创建connect,connect.sourceSignal -> RACSignal(原始信号) connect.signal -> RACSubject // 2.订阅connect.signal,会调用RACSubject的subscribeNext,创建订阅者,而且把订阅者保存起来,不会执行block。 // 3.[connect connect]内部会订阅RACSignal(原始信号),并且订阅者是RACSubject // 3.1.订阅原始信号,就会调用原始信号中的didSubscribe // 3.2 didSubscribe,拿到订阅者调用sendNext,其实是调用RACSubject的sendNext // 4.RACSubject的sendNext,会遍历RACSubject所有订阅者发送信号。 // 4.1 因为刚刚第二步,都是在订阅RACSubject,因此会拿到第二步所有的订阅者,调用他们的nextBlock // 需求:假设在一个信号中发送请求,每次订阅一次都会发送请求,这样就会导致多次请求。 // 解决:使用RACMulticastConnection就能解决. // 1.创建请求信号 RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id
subscriber) { NSLog(@"发送请求"); return nil; }]; // 2.订阅信号 [signal subscribeNext:^(id x) { NSLog(@"接收数据"); }]; // 2.订阅信号 [signal subscribeNext:^(id x) { NSLog(@"接收数据"); }]; // 3.运行结果,会执行两遍发送请求,也就是每次订阅都会发送一次请求 // RACMulticastConnection:解决重复请求问题 // 1.创建信号 RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id
subscriber) { NSLog(@"发送请求"); [subscriber sendNext:@1]; return nil; }]; // 2.创建连接 RACMulticastConnection *connect = [signal publish]; // 3.订阅信号, // 注意:订阅信号,也不能激活信号,只是保存订阅者到数组,必须通过连接,当调用连接,就会一次性调用所有订阅者的sendNext: [connect.signal subscribeNext:^(id x) { NSLog(@"订阅者一信号"); }]; [connect.signal subscribeNext:^(id x) { NSLog(@"订阅者二信号"); }]; // 4.连接,激活信号 [connect connect];
  • RACScheduler:RAC中的队列,用GCD封装的。
  • RACUnit:表示stream不包含有意义的值,也就是看到这个,可以直接理解为nil
  • RACEvent:把数据包装成信号事件(signal event)。它主要通过RACSignal的-materialize来使用。

7、ReactiveCocoa开发中常见用法

  • 代替代理
    • rac_signalForSelector:用于代替代理
  • 代替KVO
    • rac_valuesAndChangesForKeyPath:用于监听某个对象的属性改变。
  • 监听事件
    • rac_signalForControlEvents:用于监听某个事件
  • 代替通知:
    • rac_addObserverForName:用于监听某个通知
  • 监听文本框文字改变
    • rac_textSignal:只要文本框发出改变就会发出这个信号
  • 处理当界面有多次请求时,需要都获取到数据时,才能展示界面
    • rac_liftSelector:withSignalsFromArray:Signals:当传入的Signals(信号数组),每一个signal都至少sendNext过一次,就会触发第一个selector参数的方法
    • 使用注意:几个信号,参数一的方法就几个参数,每个参数对应信号发出的数据
  • 代码演      // 1.代替代理    // 需求:自定义redView,监听红色view中按钮点击
// 之前都是需要通过代理监听,给红色View添加一个代理属性,点击按钮的时候,通知代理做事情    // rac_signalForSelector:把调用某个对象的方法的信息转换成信号,就要调用这个方法,就会发送信号。    // 这里表示只要redV调用btnClick:,就会发出信号,订阅就好了。    [[redV rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) {        NSLog(@"点击红色按钮");    }];    // 2.KVO    // 把监听redV的center属性改变转换成信号,只要值改变就会发送信号    // observer:可以传入nil    [[redV rac_valuesAndChangesForKeyPath:@"center" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {        NSLog(@"%@",x);    }];    // 3.监听事件    // 把按钮点击事件转换为信号,点击按钮,就会发送信号    [[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {        NSLog(@"按钮被点击了");    }];    // 4.代替通知    // 把监听到的通知转换信号    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {        NSLog(@"键盘弹出");    }];    // 5.监听文本框的文字改变   [_textField.rac_textSignal subscribeNext:^(id x) {       NSLog(@"文字改变了%@",x);   }];   // 6.处理多个请求,都返回结果的时候,统一做处理.    RACSignal *request1 = [RACSignal createSignal:^RACDisposable *(id
subscriber) { // 发送请求1 [subscriber sendNext:@"发送请求1"]; return nil; }]; RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id
subscriber) { // 发送请求2 [subscriber sendNext:@"发送请求2"]; return nil; }]; // 使用注意:几个信号,参数一的方法就几个参数,每个参数对应信号发出的数据。 [self rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalsFromArray:@[request1,request2]];} /** * 更新UI */- (void)updateUIWithR1:(id)data r2:(id)data1{ NSLog(@"更新UI%@ %@",data,data1);}

 

8、ReactiveCocoa常见宏

  • RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某个属性绑定。
  • // 只要文本框文字改变,就会修改label的文字    RAC(self.labelView,text) = _textField.rac_textSignal;

     

  • RACObserve(self, name):监听某个对象的某个属性,返回的是信号
  • [RACObserve(self.view, center) subscribeNext:^(id x) {        NSLog(@"%@",x);    }];

     

  • @weakify(obj) 和 @strongify(obj),一般两个都是配套使用,在主头文件(ReactiveCocoa.h)中并没有导入,需要自己手动导入,RACEXTcope.h 才可以使用。但是每次导入都非常麻烦,只需要在猪头文件自己导入就好了。
  • RACTuplePack:把数据包装成RACTuple(元祖类)
  • // 把参数中的数据包装成元组    RACTuple *tuple = RACTuplePack(@10,@20);

     

  • RACTupleUnpack:把RACTuple(元祖类)解包成对应的数据。
  • // 把参数中的数据包装成元组    RACTuple *tuple = RACTuplePack(@"xmg",@20);    // 解包元组,会把元组的值,按顺序给参数里面的变量赋值    // name = @"xmg" age = @20    RACTupleUnpack(NSString *name,NSNumber *age) = tuple;

     

  • 后续还会研究一段时候后的小demo,敬请期待...

转载于:https://www.cnblogs.com/shensq/p/5482094.html

你可能感兴趣的文章
3.0.35 platform 设备资源和数据
查看>>
centos redis 安装过程,解决办法
查看>>
IOS小技巧整理
查看>>
WebDriverExtensionsByC#
查看>>
我眼中的技术地图
查看>>
lc 145. Binary Tree Postorder Traversal
查看>>
sublime 配置java运行环境
查看>>
在centos上开关tomcat
查看>>
重启rabbitmq服务
查看>>
正则表达式(进阶篇)
查看>>
无人值守安装linux系统
查看>>
【传道】中国首部淘宝卖家演讲公开课:农业本该如此
查看>>
jQuery应用 代码片段
查看>>
MVC+Servlet+mysql+jsp读取数据库信息
查看>>
黑马程序员——2 注释
查看>>
用OGRE1.74搭建游戏框架(三)--加入人物控制和场景
查看>>
转化课-计算机基础及上网过程
查看>>
android dialog使用自定义布局 设置窗体大小位置
查看>>
ionic2+ 基础
查看>>
互联网模式下我们更加应该“专注”
查看>>