iOS Reactive Cocoa使用浅析

Reactive Cocoa浅析

ReactiveCocoa是响应式编程(FRP)在IOS中的一个实现框架,Github地址

1.什么是响应式编程

推荐一个网址响应式编程(Reactive Programming)介绍,如果不太清楚,那么我们首先来了解一下其他的编程思想

1.面向过程:

“面向过程”(Procedure Oriented)是一种以过程为中心的编程思想。“面向过程”也可称之为“面向记录”编程思想,他们不支持丰富的“面向对象”特性(比如继承、多态),并且它们不允许混合持久化状态和域逻辑。

C语言就是一门面向过程的语言。

2.面向对象

面向对象语言(Object-Oriented Language)是一类以对象作为基本程序结构单位的程序设计语言,指用于描述的设计是以对象为核心,而对象是程序运行时刻的基本成分。语言中提供了类、继承等成分。

万物皆对象

典型的面向对象的编程语言有C++,C#,Java等

3.链式编程

是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好
Objective-C中的形式类似于下面这种形式

Person *per = [[Person allo] init];
per.eat().drink().sleep();

用一个计算器Demo来简单说明什么是链式编程
这是我写的一个非常简单的计算器
Calculator.h文件

#import <Foundation/Foundation.h>

@class Calculator;
typedef Calculator *(^CalculatorBlock)(double num);

@interface Calculator : NSObject

+ (instancetype)opeartionNum:(double)num;

@property (assign, nonatomic) double opeartionNum;

//加--减--乘--除

@property (copy, nonatomic) CalculatorBlock add;
@property (copy, nonatomic) CalculatorBlock reduce;
@property (copy, nonatomic) CalculatorBlock multiply;
@property (copy, nonatomic) CalculatorBlock divide;

@end

Calculator.m文件

#import "Calculator.h"

@implementation Calculator

- (instancetype)init
{
    self = [super init];
    if (self) {
        [self buildBlock];
    }
    return self;
}

+ (instancetype)opeartionNum:(double)num {
    Calculator *calu = [[Calculator alloc] init];
    calu.opeartionNum = num;
    
    return calu;
}

- (void)buildBlock {
    __weak typeof(self) weakSelf = self;
    _add = ^Calculator *(double num){
        weakSelf.opeartionNum += num;
        return weakSelf;
    };
    
    _reduce = ^Calculator *(double num){
        weakSelf.opeartionNum -= num;
        return weakSelf;
    };
    
    _multiply = ^Calculator *(double num){
        weakSelf.opeartionNum *= num;
        return weakSelf;
    };
    
    _divide = ^Calculator *(double num){
        //除数为0 不作处理
        weakSelf.opeartionNum = _opeartionNum / (num == 0 ? 1 : num);
        return weakSelf;
    };
}
@end

这个Demo非常简单,说的只是链式编程的一个思想,从中我们可以看到, 四个Block,返回的均是当前对象,
我们怎么使用呢

//开始计算
    Calculator *calu = [Calculator opeartionNum:10.00];
    calu.add(2).reduce(2).multiply(2).divide(0);
    //输出结果
    NSLog(@"result : %f",calu.opeartionNum);

3.响应式编程

我们先看看KVO(Key-Value Observing),直译过来也就是键值观察者模式,监听某个属性的变化,每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。

//注册观察者和被观察的属性路径
[xiaoming addObserver:self forKeyPath:@"age" options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

//接受属性变化的消息
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
 {
     if ([keyPath isEqualToString:@"age"] && object == xiaoming) {
        NSLog(@"小明的年龄发生变化");
     }
 }

接下来介绍一下iOS中一个响应式编程框架ReactiveCocoa
关于响应式编程的概念,我推荐看看上面推荐的那篇帖子响应式编程(Reactive Programming)介绍,举的例子是RxJS,不过整体的概念与ReactiveCocoa无异,吸收思想即可。


2.ReactiveCocoa响应式编程框架


ReactiveCocoa中常用到的是RACSignal,RACSignal继承自RACStream,Stream这个概念,直译过来也就是事件流的意思,那何为数据流,事件流就是一个按时间排序的Events序列,在这里引用响应式编程(Reactive Programming)介绍其中的介绍,
响应式编程使用异步数据流进行编程
举个通俗的例子说明事件流:如何把大象塞进冰箱?

  1. 打开冰箱门
  2. 把大象塞进冰箱
  3. 关上冰箱门

上述三个步骤组合起来就成为一个按时间排序的时间序列,即事件流


1.ReactiveCocoa的核心方法


打开RACStream可以看到

// Lazily binds a block to the values in the receiver.
///
/// This should only be used if you need to terminate the bind early, or close
/// over some state. -flattenMap: is more appropriate for all other cases.
///
/// block - A block returning a RACStreamBindBlock. This block will be invoked
///         each time the bound stream is re-evaluated. This block must not be
///         nil or return nil.
///
/// Returns a new stream which represents the combined result of all lazy
/// applications of `block`.

- (instancetype)bind:(RACStreamBindBlock (^)(void))block;

关于其中各个属性的意思,可以看RACStreamBindBlock介绍和使用,其中对于各个属性有详细的介绍,bind这个思想理解即可,RAC已经封装了很多更方便的方法供我们使用。


2.ReactiveCocoa的整体结构介绍


ReactiveCocoa整体可以分为四个部分

  1. 信号源(事件流) RACStream
  2. 订阅者 RACSubscriber
  3. 调度器 RACScheduler
  4. 清洁工 RACDisposable

1.信号源(事件流) RACStream

举个例子:在一个具有多重UI状态的界面中,我们要实现一下几个功能

  • 1 用户名和密码输入框字数超过限制时改变输入框背景颜色
  • 2 用户名和密码无效和有效时登录按钮的背景颜色设置

拿第一个问题当做例子
我们平时怎么实现的呢,无非就是继承UITextFieldDelegate,在代理中判断输入框中的文字个数是否超过限制,然后改变背景颜色,亦或用KVO实现,这样做无可厚非,但是模板化过于严重,我们想做这个功能,需要声明代理,实现代理方法,同时代理方法中还需要判断delegate中的textfield是用户名输入框还是密码输入框,这样做不仅代码不仅冗长,而且会造成代码不易查看。
那么在RAC中我们怎么做呢,看代码

 //将ViewModel中的属性和控件中的属性绑定到一起
RAC(_userViewModel,userName) = [_userNameFeild rac_textSignal];
RAC(_userNameFeild,backgroundColor) = [_userNameFeild.rac_textSignal  
map:^id(NSString *password) {
        return weakSelf.userViewModel.isUserNameValid ? [UIColor whiteColor] : [UIColor redColor];
    }];

Tips_1: 这一部分我们可以配合MVVM设计模式使用,将ViewModel与控件绑定到一起,减轻ViewController的负担。
具体这一部分的Demo可以查看文末的Github链接,只是一个简单使用
Tips_2: 由上可以看到,我们可以将某个功能的代码写的更加集中,更加方便阅读和维护


2.订阅者 RACSubscriber

在整个RAC中,我们基本上都是采用信号源-订阅者的这种模式来使用RAC,为了获取到信号源中的value,我们需要订阅这个信号源。

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

Tips: 我们查看源代码可以发现_userNameFeild.rac_textSignal实现过程就是监听所有的UIControlEventAllEditingEvents事件,然后发出信号,然后我们订阅这个信号源,可以获取到其中的值x

- (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)];
}

好了,上面讲了那么多理论知识,具体如何使用请看下面


3.调度器RACSubscriber 和 清洁工RACDisposable
  • 1 调度器RACSubscriber,这一部分是使用GCD的串行队列来实现的,文末Github链接中有对其的简单使用
  • 2 清洁工RACDisposable

    RACDisposableReactiveCocoa 中就充当着清洁工的角色,它封装了取消和清理一次订阅所必需的工作。它有一个核心的方法 -dispose ,调用这个方法就会执行相应的清理工作,这有点类似于 NSObject-dealloc 方法。

:我们上面使用subscribeNext:^(id x) {}这个方法订阅信号源进行处理之后会生成一个清洁工RACDisposable,我们可以手动的调用dispose方法来进行清理工作。


Reactive Cocoa使用


* 1 RAC发送消息,并且绑定到控件

- (void)racSenderMessage {
    //延迟2.0S 发送"呵呵哒~"消息
    RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"呵呵哒~"];
        [subscriber sendCompleted];
        return nil;
    }] delay:2.0];
    //将_userNameFeild的`text`属性与映射后的信号量的值绑定到一起
    RAC(_userNameFeild,text) = [signal map:^id(NSString *value) {
        if ([value isEqualToString:@"呵呵哒~"]) {
            return @"么么哒~";
        }
        return nil;
    }];
}

* 2 RAC代理

- (void)racProtocol {
    RACSignal *programmerSignal = [self rac_signalForSelector:@selector(whoAmI) fromProtocol:@protocol(Programmer)];
    
    [programmerSignal subscribeNext:^(id x) {
        NSLog(@"RAC通知------I'm a great programmer...");
    }];
    @weakify(self);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self_weak_ whoAmI];
    });
}

- (void)whoAmI {
    NSLog(@"whoAmI------my name is %@",_user.userName);
}

* 3 RAC通知

- (void)racNotification {
    //接受通知并且处理
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"RAC_Notifaciotn" object:nil] subscribeNext:^(NSNotification *notify) {
        NSLog(@"notify.content = %@",notify.userInfo[@"content"]);
    }];
    
    //发出通知
    [[NSNotificationCenter defaultCenter] postNotificationName:@"RAC_Notifaciotn" object:nil userInfo:@{@"content" : @"i'm a notification"}];
}

* 4 RAC信号拼接

- (void)racSignalLink {
    RACSignal *signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@(1)];
        [subscriber sendCompleted];
        return nil;
    }];
    
    RACSignal *signal2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@(2)];
        [subscriber sendCompleted];
        
        return nil;
    }];
    
    [[signal1 concat:signal2] subscribeNext:^(NSNumber *value) {
        NSLog(@"RAC信号拼接------value = %@",value);
    }];;
}

输出结果为:

2016-07-02 14:34:16.148 ReactiveCocoaMVVMDemo[1362:1245988] RAC信号拼接------value = 1
2016-07-02 14:34:16.148 ReactiveCocoaMVVMDemo[1362:1245988] RAC信号拼接------value = 2

* 5 RAC信号合并

- (void)racSignalMerge {
    RACSignal *signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"清纯妹子"];
        [subscriber sendCompleted];
        return nil;
    }];
    
    RACSignal *signal2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"性感妹子"];
        [subscriber sendCompleted];
        
        return nil;
    }];
    
    [[signal1 merge:signal2] subscribeNext:^(id x) {
        NSLog(@"RAC信号合并------我喜欢: %@",x);
    }];
}

输出结果为:

2016-07-02 14:34:16.149 ReactiveCocoaMVVMDemo[1362:1245988] RAC信号合并------我喜欢: 清纯妹子
2016-07-02 14:34:16.149 ReactiveCocoaMVVMDemo[1362:1245988] RAC信号合并------我喜欢: 性感妹子

* 6 RAC信号组合

- (void)racSignalCombine {
    RACSignal *signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"年轻"];
        [subscriber sendNext:@"清纯"];
        [subscriber sendCompleted];
        return nil;
    }];
    
    RACSignal *signal2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"温柔"];
        [subscriber sendCompleted];
        
        return nil;
    }];
    
    //combineLatest 将数组中的信号量发出的最后一个object 组合到一起
    [[RACSignal combineLatest:@[signal1, signal2]] subscribeNext:^(RACTuple *x) {
        //先进行解包
        RACTupleUnpack(NSString *signal1_Str, NSString *signal2_Str) = x;
        NSLog(@"RAC信号组合------我喜欢 %@的 %@的 妹子",signal1_Str,signal2_Str);
    }];
    
    //会注意收到 组合方法后还可以跟一个Block  /** + (RACSignal *)combineLatest:(id<NSFastEnumeration>)signals reduce:(id (^)())reduceBlock */
    /*
     reduce这个Block可以对组合后的信号量做处理
     */
    //我们还可以这样使用
    [[RACSignal combineLatest:@[signal1, signal2] reduce:^(NSString *signal1_Str, NSString *signal2_Str){
        return [signal1_Str stringByAppendingString:signal2_Str];
    }] subscribeNext:^(id x) {
        NSLog(@"RAC信号组合(Reduce处理)------我喜欢 %@ 的妹子",x);
    }];
}

输出结果为:

2016-07-02 14:34:16.149 ReactiveCocoaMVVMDemo[1362:1245988] RAC信号合并------我喜欢: 性感妹子
2016-07-02 14:34:16.150 ReactiveCocoaMVVMDemo[1362:1245988] RAC信号组合------我喜欢 清纯的 温柔的 妹子
2016-07-02 14:34:16.150 ReactiveCocoaMVVMDemo[1362:1245988] RAC信号组合(Reduce处理)------我喜欢 清纯温柔 的妹子

* 7 RAC信号组合

- (void)racSignalZIP {
    RACSignal *signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"年轻"];
        [subscriber sendNext:@"清纯"];
        [subscriber sendCompleted];
        return nil;
    }];
    
    RACSignal *signal2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"温柔"];
        [subscriber sendCompleted];
        
        return nil;
    }];
    
    //zip 默认会取信号量的最开始发送的对象 所以结果会是 年轻 、温柔
    [[RACSignal zip:@[signal1,signal2]] subscribeNext:^(RACTuple *x) {
        RACTupleUnpack(NSString *signal1_Str1,NSString *signal2_Str) = x;
        NSLog(@"RAC信号压缩------我喜欢 %@的 %@的 妹子",signal1_Str1, signal2_Str);
    }];
}

输出结果为:

2016-07-02 14:34:16.151 ReactiveCocoaMVVMDemo[1362:1245988] RAC信号压缩------我喜欢 年轻的 温柔的 妹子

* 8 RAC信号过滤

- (void)racSignalFilter {
    //信号过滤可以参考上面UIButton引用RAC的实例
    [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@(19)];
        [subscriber sendNext:@(12)];
        [subscriber sendNext:@(20)];
        [subscriber sendCompleted];
        
        return nil;
    }] filter:^BOOL(NSNumber *value) {
        if (value.integerValue < 18) {
            //18禁
            NSLog(@"RAC信号过滤------FBI Warning~");
        }
        return value.integerValue > 18;
    }]
      subscribeNext:^(id x) {
        NSLog(@"RAC信号过滤------年龄:%@",x);
   }];
    
}

输出结果为:

2016-07-02 14:34:16.151 ReactiveCocoaMVVMDemo[1362:1245988] RAC信号过滤------年龄:19
2016-07-02 14:34:16.151 ReactiveCocoaMVVMDemo[1362:1245988] RAC信号过滤------FBI Warning~
2016-07-02 14:34:16.152 ReactiveCocoaMVVMDemo[1362:1245988] RAC信号过滤------年龄:20

* 9 RAC信号传递

- (void)racSignalPass {
    [[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"老板向我扔过来一个Star"];
        return nil;
    }] flattenMap:^RACStream *(NSString *value) {
        NSLog(@"RAC信号传递------%@",value);
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"我向老板扔回一块板砖"];
            return nil;
        }];
    }] flattenMap:^RACStream *(NSString *value) {
        NSLog(@"RAC信号传递------%@",value);
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"我跟老板正面刚~,结果可想而知"];
            return nil;
        }];
    }] subscribeNext:^(id x) {
        NSLog(@"RAC信号传递------%@",x);
    }];
}

输出结果为:

2016-07-02 14:34:16.152 ReactiveCocoaMVVMDemo[1362:1245988] RAC信号传递------老板向我扔过来一个Star
2016-07-02 14:34:16.152 ReactiveCocoaMVVMDemo[1362:1245988] RAC信号传递------我向老板扔回一块板砖
2016-07-02 14:34:16.152 ReactiveCocoaMVVMDemo[1362:1245988] RAC信号传递------我跟老板正面刚~,结果可想而知

* 10 RAC信号串

//用那个著名的脑筋急转弯说明吧,“如何把大象放进冰箱里”  第一步,打开冰箱;第二步,把大象放进冰箱;第三步,关上冰箱门。
- (void)racSignalQueue {
    //与信号传递类似,不过使用 `then` 表明的是秩序,没有传递value
    [[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSLog(@"RAC信号串------打开冰箱");
        [subscriber sendCompleted];
        return nil;
    }] then:^RACSignal *{
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            NSLog(@"RAC信号串------把大象放进冰箱");
            [subscriber sendCompleted];
            return nil;
        }];
    }] then:^RACSignal *{
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            NSLog(@"RAC信号串------关上冰箱门");
            [subscriber sendCompleted];
            return nil;
        }];
    }] subscribeNext:^(id x) {
        NSLog(@"RAC信号串------Over");
    }];
}

输出结果为:

2016-07-02 17:19:53.812 ReactiveCocoaMVVMDemo[1567:2887515] RAC信号串------打开冰箱
2016-07-02 17:19:53.812 ReactiveCocoaMVVMDemo[1567:2887515] RAC信号串------把大象放进冰箱
2016-07-02 17:19:53.812 ReactiveCocoaMVVMDemo[1567:2887515] RAC信号串------关上冰箱门

* 11 RAC_Command介绍

- (void)racCommandDemo {
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            NSLog(@"racCommandDemo------亲,帮我带份饭~");
            [subscriber sendCompleted];
            return nil;
        }];
    }];
    
    //命令执行
    [command execute:nil];
}

输出结果为:

2016-07-02 17:19:53.839 ReactiveCocoaMVVMDemo[1567:2887515] racCommandDemo------亲,帮我带份饭~

* 12 RACSignal 的一些修饰符

- (void)racSignalOther {
    
    //延迟
    [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"RAC信号延迟-----等等我~等等我2秒"];
        [subscriber sendCompleted];
        return nil;
    }] delay:2.0] subscribeNext:^(id x) {
        NSLog(@"RAC信号延迟-----终于等到你~");
    }];
    
    //定时任务,可以代替NSTimer,可以看到`RACScheduler`使用GCD实现的
    [[RACSignal interval:60*60 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(id x) {
        NSLog(@"每小时吃一次药,不要放弃治疗");
    }];
    
    //设置超时时间
    [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"hh"];
            [subscriber sendCompleted];
            return nil;
        }] delay:4] subscribeNext:^(id x) {
            NSLog(@"RAC设置超时时间------请求到数据:%@",x);
            [subscriber sendNext:[@"RAC设置超时时间------请求到数据:" stringByAppendingString:x]];
            [subscriber sendCompleted];
        }];
        
        return nil;
    }] timeout:3 onScheduler:[RACScheduler mainThreadScheduler]]
        subscribeNext:^(id x) {
            //在timeout规定时间之内接受到信号,才会执行订阅者的block
            //这这里3秒之内没有接受到信号,所有该次订阅已失效
            NSLog(@"请求到的数据:%@",x);
    }];
    
    //设置retry次数,这部分可以和网络请求一起用
    __block int retry_idx = 0;
    [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        if (retry_idx < 3) {
            retry_idx++;
            [subscriber sendError:nil];
        }else {
            [subscriber sendNext:@"success!"];
            [subscriber sendCompleted];
        }
        return nil;
    }] retry:3] subscribeNext:^(id x) {
        NSLog(@"请求:%@",x);
    }];
    
    //节流阀,throttle秒内只能通过1个消息
    [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"6"];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [subscriber sendNext:@"66"];
        });
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [subscriber sendNext:@"666"];
            [subscriber sendCompleted];
        });
        
        return nil;
    }] throttle:2] subscribeNext:^(id x) {
        //throttle: N   N秒之内只能通过一个消息,所以@"66"是不会被发出的
        NSLog(@"RAC_throttle------result = %@",x);
    }];
    
    //条件控制
    /**
     解释:`takeUntil:(RACSignal *)signalTrigger` 只有当`signalTrigger`这个signal发出消息才会停止
     */
    [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(id x) {
            //每秒发一个消息
            [subscriber sendNext:@"RAC_Condition------吃饭中~"];
        }];
        return nil;
    }] takeUntil:[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        //延迟3S发送一个消息,才会让前面的信号停止
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"RAC_Condition------吃饱了~");
            [subscriber sendNext:@"吃饱了"];
        });
        return nil;
    }]] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
}

输出结果为:

//延迟
2016-07-02 17:36:09.398 ReactiveCocoaMVVMDemo[2040:2898007] RAC信号延迟-----终于等到你~
//定时任务
2016-07-02 16:36:12.400 ReactiveCocoaMVVMDemo[2040:2898007] 每小时吃一次药,不要放弃治疗
2016-07-02 17:36:12.400 ReactiveCocoaMVVMDemo[2040:2898007] 每小时吃一次药,不要放弃治疗
//设置retry次数,这部分可以和网络请求一起用
2016-07-02 17:36:07.427 ReactiveCocoaMVVMDemo[2040:2898007] 请求:success!
//节流阀,throttle秒内只能通过1个消息
2016-07-02 17:36:10.404 ReactiveCocoaMVVMDemo[2040:2898007] RAC_throttle------result = 66
2016-07-02 17:36:10.404 ReactiveCocoaMVVMDemo[2040:2898007] RAC_throttle------result = 666
//takeUntil条件控制
2016-07-02 18:10:52.074 ReactiveCocoaMVVMDemo[3090:2920422] RAC_Condition------吃饭中~
2016-07-02 18:10:53.078 ReactiveCocoaMVVMDemo[3090:2920422] RAC_Condition------吃饭中~
2016-07-02 18:10:54.074 ReactiveCocoaMVVMDemo[3090:2920422] RAC_Condition------吃饭中~
2016-07-02 18:10:55.077 ReactiveCocoaMVVMDemo[3090:2920422] RAC_Condition------吃饭中~
2016-07-02 18:10:55.078 ReactiveCocoaMVVMDemo[3090:2920422] RAC_Condition------吃饱了~

文末

本篇文章并未很深的探讨ReactiveCocoa的底层实现方法,权当做抛砖引玉,给大家介绍一下RAC的浅析和使用


Github链接和文中所引用的文章链接


如果觉得文章对你有所帮助的话,请点个Star啦~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容