ReactiveCocoa综合

ReactiveCocoa综合_第1张图片

最终诉求

整理层次结构,一个理论一个demo,链式编程、响应式编程、RAC内部原理、编程思想、RAC梳理直播逻辑、锻炼头脑、帮助他人、深入浅出、swift基础。

Cocoa框架

面向对象的框架,OC是面向对象的编程,本质上是对于c语言命令式编程的上层封装。

函数式编程Vs面向对象编程

面向对象编程:指令式编程范畴。函数式编程: 语法趋向于数学公式的推导,代码执行顺序不重要,意味着轻松并发编程。响应式编程典型例子:Excel的一个单元格变化导致其它单元格立即变化。Autolayout的父视图frame变化,有约束关系的子视图frame随之变化。一切皆因变量之间存在着函数关系。

RAC改写登录注册

登录service校验用户名和密码、登录按钮不可点击。防止用户多次登录。如果登录失败、用户尝试重新登录、隐藏错误信息。改变按钮状态、接收到next值信号、副作用操作。

高阶函数

函数里面调用函数。在oc中,blocks是被广泛使用的参数传递,它实际上是匿名函数。像linux命令里的pipeline(管道),一个命令调用后的输出当作另一个命令的输入,多个命令串起来操作。

链式编程

返回相同类型或子类或其它类就可以链式调用,图灵等价,语言是基于需求的,没有高低之分,变量不变,意味着代码没顺序要求,线程安全,编译器更好优化,不使用变量,要么递归,要么顺推,折叠函数,要来一个比较函数,高阶函数利用,首先写一个Block,高阶函数帮我门生成一个新的函数。链式编程的思想,即将多个操作通过.连接起来,使得可读性更强,是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好。a(1).b(2).c(3)。特点:方法的返回值是block,block必须有返回值(本身对象),block参数(需要操作的值)。代表:masonry框架。在swift中提供了包括map、filter、reduce等十分简洁优秀的高阶函数供我们对数组数据进行操作,同样情况下,遍历一个数组并求和在使用oc(不使用kvc)和swift的环境下的代码是这样的。无论是代码量还是简洁性,此时的oc都比不上swift。那么接下来就要通过神奇的block来为oc添加这些高阶函数的实现

函数式编程

是把操作尽量写成一系列嵌套的函数或者方法调用。代表:ReactiveCocoa。特点:每个方法必须有返回值(本身对象),把函数或者Block当做参数,block参数(需要操作的值)block返回值(操作结果)

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

本质通过数据流表达应用功能。关注改变状态值的行为事件,这一系列事件组成了事件流。类似于OC的观察者设计模式。命令式编程语言中,a: = b + c 表示 a 是 b + c 表达式的值。但在RP语言中,表示动态数据流关系:只要c或b的值发生变化,a的值自动跟着变化。不需使用实例变量追踪瞬时状态,新输入值到达,函数立即根据最新输入值计算产生新输出值。

ReactiveCocoa优点

函数响应式编程(FRP)框架。ReactiveCocoa为事件提供了很多处理方法,处理事件很方便,可以把要处理的事情,和监听的事情的代码放在一起,这样非常方便我们管理,就不需要跳到对应的方法里。非常符合我们开发中高聚合,低耦合的思想。使用RAC解决问题,不需考虑调用顺序,直接考虑结果,把原本分离的操作写成嵌套进一起,使代码高聚合,方便管理。

RACSignal

简介:ReactiveCocoa signal(RACSignal)信号源发送信号给它的subscriber订阅者。主要发送三种信号:next值信号、error错误信号、completed结束信号。不同信号进入订阅者不同Block代码块。UIKit控件使用RAC框架的类别即可将空间的事件回调变成信号发送。原本是响应事件,现在是订阅信号。

  • 创建单元信号:RACSignal类方法创建单元信号对象。

  • 创建动态信号:Block作为形参把信号订阅后需要执行的系列任务记录下来,传入RACSignal的类方法创建动态信号。形参Block的输入值就是订阅者RACSubscriber,意味着只要信号被订阅就可以执行Block里面的操作。

  • Cocoa桥接:RAC将Cocoa框架中的断点、事件、触发转变成信号,简直棒到没朋友!程序开始调用某方法时发送信号、控件接收到手势触发事件时发送信号、控件的特定属性发生改变时发送信号。所发送信号的值绑定到属性上。

  • 信号变换:常见需求,当我订阅了信号并接收到信号的值,但是信号值与期望值还有点距离,那么必不可少的就是信号变换,本质上就是变换信号的值。

  • 序列转换:sequence是个什么鬼,为啥sequence的signal属性就是序列装换?序列装换有啥用呀?

  • 信号订阅:控件的手势触发、指定方法开始调用都会发出信号

RACDisposable

类方法disposableWithBlock、销毁RACSignal信号对象、形参Block代码块封装信号的订阅者消失后所需要执行的代码(取消观察者Or取消代理)

信号的订阅者消失

订阅者发送error信号、订阅者发送Completed信号、订阅者的生命周期调用了dealloc

SubscribeNext方法的内部实现逻辑

创建订阅者subscriber(代号007)、订阅者保存Block形参、Block代码块就是从更大的一段代码块中抽离出来、订阅者要做的事情就是决定什么地方执行这个代码块

RACCompoundDisposable

RACDisposable的子类、方法addDisposable添加RACDisposable、block块执行清理操作

单个信号变换

值,数量,维度,时间间隔

改变信号的值

Map:遍历每个信号、block改变了next信号的值

Aggregate :累加、全局变量存储累加值但不符合函数式编程、折叠函数不停向下传递累加值、伪递归不用存储栈里的中间变量

Scan:累加值没更新一次就发送一个新信号

MapReplace:next信号的值通通替换成固定值

ReduceEach:如果next信号的值是元祖、实现元祖的元素内部运算

改变信号数量

Take:只要几个信号。顾名思义就是拿的意思,拿什么,当然那信号源A里面的信号,拿几个,当然是自己定。不过注意的是,拿几个信号可不是随便拿,是严格按照信号产生的先后顺序来拿,当然不会拿已经产生很久的信号,肯定是拿最新的信号啦!

Skip:跳过第一个信号不要,顾名思义跳跃,跳跃什么,当然跳过信号啦,跳过最新出现的信号,至于跳过几个最新产生的信号,全屏自己来决定

startWith:增加一个信号。表示给信号源A添加一个自定义的信号,然后注意这个自定义的信号为开头,再组合信号A其它所有的信号,于是这样就组成了信号B。

repeat:循环出现已有的信号、无论什么信号。表示将信号源A里面的所有信号进行不断地重复,这样就是形成了一个新的信号源B,只是这个信号源A里面的信号总共会重复多少次呢,而且如果信号源A在无限循环重复的过程中,出现了新的信号怎么破?

retry:循环出现已有的信号、碰到错误信号重新循环。表示信号A有一个信号,然后来订阅信号源A里面的这个信号,重复订阅的意思,这就迷茫了,不是应该是操作信号源A里面的信号数量么,怎么搞成去订阅信号源A里面的信号了,简直不合常理。

throttle:阀门、重复点击不多次请求

Filter:可以设定条件过滤信号源A中不符合条件的信号,本质就是把信号源A的每个信号里的值都拿出来判断一下,如果这个信号的值满足所设定的判断,那么就把这个信号保留下来变成了信号源B所发的信号

Ignore:可以删除信号源A中指定值的信号。其实本质就是把信号源A发的每一个信号的值都拿出来判断一下,如果判断到从信号中取出的那个值就我所设定的值,那么把这个信号从信号源A中给删除掉,这样就生成了新的信号源B

takeLast:就是去拿信号源A中的最后几个信号

takeUntilBlock:一直去信号源A哪里拿信号直到下一个要拿的信号的值已经我们设定的判断条件

takeWhileBlock:判断信号源A中的每一个信号,哪一个信号的值满足我们设定的条件,我们就是从这个信号以下通通拿走组成一个新的信号源B

skipUntilBlock:本来信号源A一直跳跃,跳过万水千山,一直不肯安定下来,直到发现了真爱,这个真爱就是发现了下一个信号的值刚好满足自己设定的条件,那么自然就是停止跳跃,安定下来,就从这里开始提取信号源A的信号

skipWhileBlock:本来信号源A一直好好的,当判断下一个信号满足什么条件后就开始跳过这个信号,有点类似于判断每一个信号的值,如果下一个信号的值满足我们设定的条件,我们就把它过滤掉的意思,当然这里的官方语言是跳过,跳过和过滤还是有区别的,过滤可能只是过滤掉这一个,但是下一个还是会接着判断的,但是跳跃可就不是这样的了,跳跃意味着以后的信号都不会再处理,直接就结束信号的操作了,被跳跃的信号后面的所有信号都将不再存在。

any和All:属于混合操作,意思就是信号源A中信号每一个信号都需要来一次判断,只有满足多个复合条件的信号才留下,否则其它通通不留。这样也会生成新的信号源B

多个信号的组合

Contact:连接

Merge:组合,考虑分线程

Zip:拉链,信号源1和信号源2交替组成新的信号

CombineLatest:两个信号源的最后值结合组成新信号

Sample:采样,采集对方信号的最后一个值

TakeUntil:控制信号去停止另一个信号

RACSubject

冷信号?

本地下载点播,每次从头播放。不论订阅者时间先后订阅到的内容相同

热信号?

电视直播,可能中途开始,订阅者角度,主动启动,被动接收。订阅者订阅到的内容严格与时间相关

RACSignal和RACSubject比较

RACScheduler

异步库、实现异步、CPU来回调度兼顾多个任务、过度使用优先级万一整出两个第一名咋整、

//创建单元信号(next值事件)
RACSignal *signal1 = [RACSignal return:@"Some Value"];
//创建单元信号(error错误事件)
RACSignal *signal2 = [RACSignal error:errorObject];
//创建单元信号(empty空事件)
RACSignal *signal3 = [RACSignal empty];
//创建单元信号(never永远事件)
RACSignal *signal4 = [RACSignal never];
//创建动态信号
RACSignal *signal5 = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    [subscriber sendNext:@1];
    [subscriber sendNext:@2];
    [subscriber sendError:errorObject];
    [subscriber sendCompleted];
    return [RACDisposable disposableWithBlock:^{

    }];
}];
//创建方法监听信号
RACSignal *signal6 = [view rac_signalForSelector:@selector(setFrame:)];
//创建点击事件信号
RACSignal *signal7 = [view rac_signalForControlEvents:UIControlEventTouchUpInside];
//创建销毁事件信号
RACSignal *signal8 = [view rac_willDeallocSignal];
//创建控件属性信号
RACSignal *signal9 = RACObserve(view, backgroundColor);
//编辑旧信号返回新信号
RACSignal *signal10 = [signal1 map:^id(NSString *value) {
   return [value substringFromIndex:1];
}];
//序列转换
RACSignal *signal11 = sequence.signal;
//订阅一个信号
[signal11 subscribeNext:^(id x) {
    NSLog(@"next value is %@", x);
} error:^(NSError *error) {
    NSLog(@"Ops! Get some error: %@", error);
} completed:^{
    NSLog(@"It finished success");
}];
//绑定
RAC(view, backgroundColor) = signal10;
[view rac_liftSelector:@selector(convertPoint:toView:) withSignals:signal1, signal2, nil];
[view rac_liftSelector:@selector(convertRect:toView:)  withSignalsFromArray:@[signal3, signal4]];
[view rac_liftSelector:@selector(convertRect:toLayer:) withSignalOfArguments:signal5];
//订阅过程
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber){
    [subscriber sendNext:@1];
    [subscriber sendNext:@2];
    [subscriber sendCompleted];
    return [RACDisposable disposableWithBlock:^{
        NSLog(@"dispose");
    }];
}];
//手动取消订阅,即使订阅者没有被销毁,仍然可以取消该信号
RACDisposable *disposable = [signal subscribeNext:^(id x) {
    NSLog(@"next value is %@", x);
} error:^(NSError *error) {
    NSLog(@"Ops! Get some error: %@", error);
} completed:^{
    NSLog(@"It finished success");
}];
[disposable dispose];
//元组RACTuple
RACTuple * tuple = RACTuplePack(@1, @"haha");
id first  = tuple.first;
id second = tuple.second;
id last   = tuple.last;
id index1 = tuple[1];
RACTupleUnpack(NSNumber *num, NSString *str) = tuple;
//值操作——Map
RACSignal *signalB = [signalA map:^id(NSNumber *value) {
   return @(value.integerValue * 2);
}];
//值操作——ReduceEach:
RACSignal *signalB = [signalA reduceEach:^id(NSNumber *first,NSNumber *second) {
   return @(first.integerValue + second.integerValue);
}];
//数量操作——Filter
RACSignal *signalB = [signalA filter:^BOOL(NSString *value) {
   return value.length > 2;
}];
//数量操作——Ignore
RACSignal *signalB = [signalA filter:^BOOL(id value) {
   return ![@1 isEqual:value];
}];
RACSignal *signalC = [signalA ignore:@1];
- (RACSignal *)ignoreValues;
- (RACSignal *)distinctUntilChanged;
//数量操作——Take
RACSignal *signalB = [signalA take:2];
//数量操作——Skip
RACSignal *signalB = [signalA skip:1];
//数量操作——Take&Skip其他
- (RACSignal *)takeLast:(NSUInteger)count;
- (RACSignal *)takeUntilBlock:(BOOL (^)(id x))predicate;
- (RACSignal *)takeWhileBlock:(BOOL (^)(id x))predicate;
- (RACSignal *)skipUntilBlock:(BOOL (^)(id x))predicate;
- (RACSignal *)skipWhileBlock:(BOOL (^)(id x))predicate;
//数量混合操作
- (RACSignal *)any;
- (RACSignal *)any:(BOOL (^)(id object))predicateBlock;
- (RACSignal *)all:(BOOL (^)(id object))predicateBlock;
//数量操作——StartWith
RACSignal *signalB = [signalA startWith:@"Start"];
//数量操作——Repeat
RACSignal *signalB = [signalA repeat];
//数量操作——Retry,如果订阅都到了错误事件,重头开始订阅
RACSignal *signalB = [signalA retry:2];
//副作用操作--就是没有改变任何变量的值的操作
RACSignal *signalB = [signalA map:^id(id value) {
   return value;
}];
RACSignal *signalC = [signalA doNext:^(id x) {
   do some thing;
}];
- (RACSignal *)doError:(void (^)(NSError *error))block;
- (RACSignal *)doCompleted:(void (^)(void))block;
- (RACSignal *)initially:(void (^)(void))block;
- (RACSignal *)finally:(void (^)(void))block;
//数量操作——Collect
RACSignal *signalB = [signalA collect];
//数量操作——Aggregate
typedef int(^FoldFunction)(int running, int next);
int fold(int *array, int count, FoldFunction func, int start){
    int current = array[0];
    int running = func(start, current);
    if (count == 1) return running;;
    return fold(array + 1, count - 1, func, running);
}
    int arr[] = {1, 2, 3, 4, 5};
    int result = fold(arr, 5, ^int(int running, int next) {
    return running + next;
}, 0);
//数量操作——Aggregate
RACSignal *signalB = [signalA aggregateWithStart:@0 reduce:^id(NSNumber *running,NSNumber *next) {
     return @(running.integerValue + next.integerValue);
}];
//值操作——Scan
RACSignal *signalB = [signalA scanWithStart:@0 reduce:^id(NSNumber *running,NSNumber *next) {
    return @(running.integerValue + next.integerValue);
}];
//Aggregate&Scan变种
- (RACSignal *)aggregateWithStart:(id)start reduceWithIndex:(id (^)(id running,id next,NSUInteger index))reduceBlock;
- (RACSignal *)aggregateWithStartFactory:(id (^)(void))startFactory reduce:(id (^)(id running,id next))reduceBlock;
- (RACSignal *)scanWithStart:(id)startingValue reduceWithIndex:(id (^)(id running,id next,NSUInteger index))reduceBlock;
//无限递增信号、斐波那契数列信号
RACSignal *repeat1 = [[RACSignal return:@1] repeat];
RACSignal *signalB = [repeat1 scanWithStart:@0 reduce:^id(NSNumber *running,NSNumber *next) {
    return @(running.integerValue + next.integerValue);
}];
RACSignal *signalC = [repeat1 scanWithStart:RACTuplePack(@1, @1) reduce:^id(RACTuple *running, id _) {
    NSNumber *next = @([running.first integerValue] + [running.second integerValue]);
    return RACTuplePack(running.second, next);
}];
//时间操作——有用的信号
+ (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler;
+ (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler  withLeeway:(NSTimeInterval)leeway;
//时间操作——Delay
RACSignal *signalB = [signalA delay:1];
RACSignal *interval = [[[RACSignal return:@1] delay:1] repeat];
//时间操作——Throttle
RACSignal *signalB = [signalA throttle:2];
//时间操作——类似Throttle的方法
- (RACSignal *)throttle:(NSTimeInterval)interval valuesPassingTest:(BOOL (^)(id next))predicate;
- (RACSignal *)bufferWithTime:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler;
//组合操作——Concat皇帝和太子,谁发号施令
RACSignal *signalC = [signalA concat:signalB];
//组合操作——Merge
RACSignal *signalC = [signalA merge:signalB];
RACSignal *signalC = [RACSignal merge:@[signalA, signalB]];
RACSignal *signalC = [RACSignal merge:RACTuplePack(signalA, signalB)];
//Merge综合应用
RACSignal *appearSignal = [[self rac_signalForSelector:@selector(viewDidAppear:)]
mapReplace:@YES];
RACSignal *disappearSignal = [[self rac_signalForSelector:@selector(viewWillDisappear:)]
mapReplace:@NO];
RACSignal *activeSignal = [RACSignal merge:@[appearSignal, disappearSignal]];
//组合操作——Zip夫妻决定一件事情必须双方都同意,各拿一个值事件组成元祖,不能重复拿值事件
RACSignal *signalC = [signalA zipWith:signalB];
RACSignal *signalC = [RACSignal zip:@[signalA, signalB]];
RACSignal *signalC = [RACSignal zip:RACTuplePack(signalA, signalB)];
//组合操作——CombineLatest各拿一个最新值事件组成一个元祖
RACSignal *signalC = [signalA combineLatestWith:signalB];
RACSignal *signalC = [RACSignal combineLatest:@[signalA, signalB]];
组合操作——Zip&CombineLatest更多操作
+ (RACSignal *)combineLatest:(id)signals reduce:(id (^)())reduceBlock;
+ (RACSignal *)zip:(id)streams reduce:(id (^)())reduceBlock;
//组合操作——Sample用B接收A最近的值事件
RACSignal *signalC = [signalA sample:signalB];
//组合操作——TakeUntil
RACSignal *signalC = [signalA takeUntil:signalB];
//组合操作——TakeUntilReplacement
RACSignal *signalC = [signalA takeUntilReplacement:signalB];
ReactiveCocoa之冷信号与热信号
//冷热信号转换本质
    return [RACSignal createSignal:^RACDisposable *(id subscriber) {
        RACStreamBindBlock bindBlock = block();
        [self subscribeNext:^(id x) {
        BOOL stop = NO;
        RACSignal *signal = (RACSignal *)bindBlock(x, &stop);
        if (signal == nil || stop) { [subscriber sendCompleted];
        } else {
        [signal subscribeNext:^(id x) { [subscriber sendNext:x];
        } error:^(NSError *error) { [subscriber sendError:error];
        } completed:^{ }];
        }
        } error:^(NSError *error) { [subscriber sendError:error];
        } completed:^{ [subscriber sendCompleted]; }];
        return nil;
    }];
}
//热信号RACSubject
RACSubject *subject = [RACSubject subject];
[subject subscribeNext:^(id x) {} error:^(NSError *error) {} completed:^{}];
[subject sendNext:@1];
[subject sendNext:@2];
[subject sendNext:@3];
[subject sendCompleted];
//RACReplaySubject带快速回播的Subject
RACReplaySubject *subject = [RACReplaySubject replaySubjectWithCapacity:1];
[subject sendNext:@1];
[subject sendNext:@2];
[subject sendCompleted];
[subject subscribeNext:^(id x) {  /* a*/  }];
//冷信号 -> 热信号:RACSubject有权对从RACSignal哪里订阅到的值事件做分发和处理
RACSignal *signal = @[@1, @2, @3, @4].rac_sequence.signal;
RACSignal *signalB = [[signal map:^id(id value) {
return [[RACSignal return:value] delay:1];
}] concat];
RACSubject *speaker = [RACSubject subject];
[signalB subscribe:speaker];
[speaker subscribeNext:^(id x) {  // a }];
[speaker subscribeNext:^(id x) {  // b }];
[speaker subscribeNext:^(id x) {  // c }];
//冷信号 -> 热信号 官方方案
- (RACMulticastConnection *)publish;
- (RACMulticastConnection *)multicast:(RACSubject *)subject;
- (RACSignal *)replay;
- (RACSignal *)replayLast;
- (RACSignal *)replayLazily;
//冷信号+副作用方法
+ (RACSignal *)defer:(RACSignal * (^)(void))block;
- (RACSignal *)then:(RACSignal * (^)(void))block;
//不建议使用的同步方法
- (id)first;
- (id)firstOrDefault:(id)defaultValue;
- (id)firstOrDefault:(id)defaultValue success:(BOOL *)success error:(NSError **)error;
- (BOOL)waitUntilCompleted:(NSError **)error;
- (NSArray *)toArray;
@property (nonatomic, strong, readonly) RACSequence *sequence;
//groupBy很多个普通值事件的信号变成包含两个信号值事件的高阶信号
- (RACSignal *)groupBy:(id (^)(id object))keyBlock;
- (RACSignal *)groupBy:(id (^)(id object))keyBlock transform:(id (^)(id object))transformBlock;
RACSignal *signal = @[@1, @2, @3, @4].rac_sequence.signal;
RACSignal *signalGroup = [signal groupBy:^NSString *(NSNumber *object) {
    return object.integerValue % 2 == 0 ? @"odd" : @"even";
}];
[[[signalGroup take:1] flatten] subscribeNext:^(id x) {
    NSLog(@"next: %@", x);
}];
同步:方法或函数不返回结果不进行下一步,事无巨细,亲力亲为
异步:分身有术
并发:统筹兼顾
并行:专人专事
// 主线程的Scheduler
RACScheduler *mainScheduler = [RACScheduler mainThreadScheduler];
// 子线程的Scheduler
RACScheduler *scheduler2 = [RACScheduler scheduler];
// 返回当前的Scheduler
RACScheduler *scheduler3 = [RACScheduler currentScheduler];
// 设置Scheduler的优先级,注意两个都是老子天下第一
RACScheduler *scheduler4 = [RACScheduler schedulerWithPriority:RACSchedulerPriorityHigh];
RACScheduler *scheduler5 = [RACScheduler schedulerWithPriority:RACSchedulerPriorityHigh name:@"someName"];
// 创建立即的Scheduler
RACScheduler  *scheduler6 = [RACScheduler immediateScheduler];
// 调度程序在mainScheduler里面执行任务,同时可以随时取消任务的执行
RACDisposable *disposable = [mainScheduler schedule:^{ /* 
// 时间之后调度程序在scheduler1里面执行
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
NSDate *date = [formatter dateFromString:@"2016-07-20 21:00:00"];
[scheduler1 after:date schedule:^{ /* 
// 秒之后调度程序在scheduler2里面执行
[scheduler2 afterDelay:30 schedule:^{ /* 
// 时间之后调度程序在scheduler3里面执行,没隔1秒执行一次,算上允许延迟的0.1秒,执行时间最长1.1秒
[scheduler3 after:[NSDate date] repeatingEvery:1 withLeeway:0.1 schedule:^{}];


RACScheduler vs GCD
//异步订阅:创建子线程Scheduler执行subscribeNext方法
[[RACScheduler scheduler] schedule:^{
    [signal subscribeNext:^(id x) {
        NSLog(@"333");
    }];
}];
//异步发送:创建子线程Scheduler执行sendNext方法
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    RACDisposable *disposable = [[RACScheduler scheduler] schedule:^{
        [subscriber sendNext:@1];
        [subscriber sendCompleted];
    }];
    return disposable;
}];
//同步发送在前异步发送在后:subscribeNext的Block代码块先在主线程执行,后在子线程执行
//一会儿主线程激活subscribeNext一会儿子线程激活subscribeNext,自己的路自己走到底
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    [subscriber sendNext:@0.1];
    RACDisposable *disposable = [[RACScheduler scheduler] schedule:^{
        [subscriber sendNext:@1.1];
        [subscriber sendCompleted];
    }];
    return disposable;
}];
//订阅和发送都异步:异步高于同步,发送优于订阅
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    [subscriber sendNext:@0.1];
    RACDisposable *disposable = [[RACScheduler scheduler] schedule:^{
        [subscriber sendNext:@1.1];
        [subscriber sendCompleted];
    }];
    return disposable;
}];
[[RACScheduler scheduler] schedule:^{
    [signal subscribeNext:^(id x) {
        NSLog(@"%@", x);
    }];
}];
//subscribeOn:保证didSubscribe的Block在指定schedule执行,比如说在didSubscribe的Block里执行添加UIKit控件到父视图的代码
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    [subscriber sendNext:@0.1];
    RACDisposable *disposable = [[RACScheduler scheduler] schedule:^{
        [subscriber sendNext:@1.1];
        [subscriber sendCompleted];
    }];
    return disposable;
}];
[[RACScheduler scheduler] schedule:^{
    [[signal subscribeOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(id x) {
        NSLog(@"%@", x);
    }];
}];
//deliverOn:保证subscribeNext的Block在指定schedule执行
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    [subscriber sendNext:@0.1];
    RACDisposable *disposable = [[RACScheduler scheduler] schedule:^{
        [subscriber sendNext:@1.1];
        [subscriber sendCompleted];
    }];
    return disposable;
}];
[[RACScheduler scheduler] schedule:^{
    [[signal deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(id x) {
        NSLog(@"%@", x);
    }];
}];
//subscribeOn:的用武之地
void whenShouldWeUseSubscribeOn()
{
UIView *view = [[UIView alloc] init];
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
UILabel *label = [[UILabel alloc] init];
label.text = @"Hello world";
[view addSubview:label];
[subscriber sendNext:@0.1];
RACDisposable *disposable = [[RACScheduler scheduler] schedule:^{
[subscriber sendNext:@1.1];
[subscriber sendCompleted];
}];
return disposable;
}];
[[RACScheduler scheduler] schedule:^{
[[signal subscribeOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
}]; }

ReactiveCocoa之信号高阶操作

//创造高阶信号
RACSignal *signal = [RACSignal return:@1];
RACSignal *signalHighOrder = [RACSignal return:signal];
RACSignal *anotherSignal = [signal map:^id(id value) {
    return [RACSignal return:value];
}];
//订阅高阶信号
RACSignal *signal = @[@1, @2, @3].rac_sequence.signal;
RACSignal *highOrderSignal = [signal map:^id(id value) {
    return [RACSignal return:value];
}];
[highOrderSignal subscribeNext:^(RACSignal *aSignal) {
    [aSignal subscribeNext:^(id x) {
        // get real value here.
    }];
}];
//降阶操作——SwitchToLatests一旦订阅到新信号的值立马销毁旧信号
RACSignal *signalB = [signalA switchToLatest]; 
RACSignal *autoRunButtonClickSignal = [self.autoRunBtn rac_signalForControlEvents:UIControlEventTouchUpInside];
RACSignal *oneStepButtonClickSignal = [self.oneStepBtn rac_signalForControlEvents:UIControlEventTouchUpInside];
RACSignal *idSignal = [RACSignal return:nil];
RACSignal *timerSignal = [RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]];
autoRunButtonClickSignal = [autoRunButtonClickSignal mapReplace:idSignal];
oneStepButtonClickSignal = [oneStepButtonClickSignal mapReplace:timerSignal];
RACSignal *controlSignal = [autoRunButtonClickSignal merge:oneStepButtonClickSignal];
controlSignal = [controlSignal switchToLatest];
//填词搜索示例,信号事件降阶成值事件,同时一旦处理新的信号事件的值事件,立即结束前一个信号事件
RACSignal *searchTextSignal = [self.searchTextField rac_textSignal];
RACSignal *requestSignals = [searchTextSignal map:^id(NSString *searchText) {
    NSString *urlString = [NSString stringWithFormat:
    @"http://xxxx.xxx.xxx/?q=%@", searchText];
    NSURL *url = [NSURL URLWithString:urlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    return [NSURLConnection rac_sendAsynchronousRequest:request];
}];
requestSignals = [requestSignals switchToLatest];
//降阶操作——If/then/else如果是YES订阅信号A如果是NO订阅信号B的原理就是switchToLatest来实现
RACSignal *signalB = [RACSignal if: signalA then: signalTrue else: signalFalse]; 
+ (RACSignal *)if:(RACSignal *)boolSignal then:(RACSignal *)trueSignal else:(RACSignal *)falseSignal {
    return [[boolSignal map:^(NSNumber *value) {
        return (value.boolValue ? trueSignal : falseSignal);
    }] switchToLatest];
}
+ (RACSignal *)switch:(RACSignal *)signal cases:(NSDictionary *)cases default:(RACSignal *)defaultSignal;
降阶操作——Flatten按照时间顺序订阅每一个信号的每一个值事件
RACSignal *signalB = [signalA flatten]; 
RACSignal *signalB = [signalA flatten:2];
RACSignal *signalB = [signalA flatten:1];
//高阶信号concat连续方法
RACSignal *signal = @[@1, @3, @7, @9, @8].rac_sequence.signal;
RACSignal *timerSignal = [[signal map:^id(id value) {
    return [[RACSignal return:value] delay:1];
}] concat];
//降阶操作总结
SwitchToLatests
Flatten(

思考1:改变某个值的个数
RACSignal *signal = @[@1, @2, @3].rac_sequence.signal;
RACSignal *mappedSignal = [[signal map:^id(NSNumber *value) {
    return [[[RACSignal return:value] repeat] take:value.integerValue];
}] flatten];
思考2:将一个值改为错误
RACSignal *signal = @[@1, @2, @3, @0].rac_sequence.signal;
RACSignal *mappedSignal = [[signal map:^id(NSNumber *value) {
    if (value.integerValue == 0) {
        return [RACSignal error:[NSError errorWithDomain:@"0" code:0 userInfo:nil]];
    }else {
        return [RACSignal return:value];
    }
}] flatten];
思考3:改变值的时间间隔
RACSignal *signal = @[@"♪5", @"♬1", @"♬2", @"♬3", @"♩4"].rac_sequence.signal;
NSDictionary *toneLengthMap = @{@"♩": @0.5, @"♪": @0.25, @"♬": @0.125};
RACSignal *mappedSignal = [[signal map:^id(NSString *value) {
    NSString *tone = [value substringFromIndex:1];
    NSString *length = [value substringToIndex:1];
    NSNumber *toneValue = @(tone.integerValue);
    NSNumber *toneLength = toneLengthMap[length];
    return [[RACSignal return:toneValue] concat:[[RACSignal empty] delay: toneLength.doubleValue]];
}] concat];
修改每一个普通值事件成为信号值事件,然后执行信号值事件的信号的flatten降阶操作
RACSignal *flatten = [signal flattenMap:^RACStream *(RACSignal *value) {
    return value;
}];
RACSignal *map = [signal flattenMap:^RACStream *(id value) {
    id anotherValue = value; // map here!
    return [RACSignal return: anotherValue];
}];
RACSignal *filter = [signal flattenMap:^RACStream *(id value) {
    BOOL filter = (value == nil); // filter here!
    return filter ? [RACSignal empty] : [RACSignal return:value];
}];
//FlattenMap网络请求数据的逻辑Demo
RACSignal *signal = [RACSignal return:@"http://xx.com/a"];
RACSignal *getSignal = [signal flattenMap: ^RACStream *(NSString *url) {
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
    return [NSURLConnection rac_sendAsynchronousRequest:request];
}];
RACSignal *jsonSignal = [getSignal flattenMap: ^RACStream *(NSData *data) {
    NSError *error = nil;
    id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
    return error == nil ? [RACSignal return: result] : [RACSignal error: error];
}];
RACSignal *getItemSignal = [jsonSignal flattenMap: ^RACStream *(NSDictionary *value) {
    if (![value isKindOfClass:[NSDictionary class]] || value[@"data.url"] == nil) {
        return [RACSignal error:someError];
    }
    NSURLRequest *anotherRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:value[@"data.url"]]];
    return [NSURLConnection rac_sendAsynchronousRequest:anotherRequest];
}];


FlattenMap与monad、bind
Functor

- (instancetype)flattenMap:(RACStream * (^)(id value))block;
- (instancetype)bind:(RACStreamBindBlock (^)(void))block;
typedef RACStream * (^RACStreamBindBlock)(id value, BOOL *stop);
• •
• •

//bind妙用
- (RACSignal *)take:(NSUInteger)count {
    if (count == 0) return [RACSignal empty];
    return [self bind:^{
        __block NSUInteger taken = 0;
        return ^ id (id value, BOOL *stop) {
            if (taken < count) {
                ++taken;
                if (taken == count) *stop = YES;
                return [class return:value];
            } else {
                return nil;
            }
        };
    }];
}


//bind简单实现和问题
- (RACSignal *)bind:(RACStreamBindBlock (^)(void))block;
{
    return [RACSignal createSignal:^RACDisposable *(id subscriber) {
    RACStreamBindBlock bindBlock = block();
    [self subscribeNext:^(id x) {
    BOOL stop = NO;
    RACSignal *signal = (RACSignal *)bindBlock(x, &stop);
    if (signal == nil || stop) { [subscriber sendCompleted];
    } else {
    [signal subscribeNext:^(id x) { [subscriber sendNext:x];
    } error:^(NSError *error) { [subscriber sendError:error];
    } completed:^{ }];
    }
    } error:^(NSError *error) { [subscriber sendError:error];
    } completed:^{ [subscriber sendCompleted]; }];
    return nil;
    }];
}
//有用的高阶操作
- (RACSignal *)try:(BOOL (^)(id value, NSError **errorPtr))tryBlock;
- (RACSignal *)tryMap:(id (^)(id value, NSError **errorPtr))mapBlock;
- (RACSignal *)catch:(RACSignal * (^)(NSError *error))catchBlock;
- (RACSignal *)catchTo:(RACSignal *)signal;
- (RACSignal *)timeout:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler;


ReactiveCocoa之生命周期详解
//RACSignal:冷信号的实例状态
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
   [subscriber sendNext:@1];
   [subscriber sendNext:@2];
   [subscriber sendCompleted];
   return nil;
}];
[signal subscribeNext:^(id x) {
    NSLog(@"next: %@", x);
} error:^(NSError *error) {
    NSLog(@"error: %@", error);
} completed:^{
    NSLog(@"complete");
}];

//RACSubject:热信号的实例状态
RACSubject *subject = [RACSubject subject];
[subject subscribeNext:^(id x) {
    NSLog(@"Subscriber 1 receive next: %@", x);
} error:^(NSError *error) {
    NSLog(@"Subscriber 1 receive error: %@", error);
} completed:^{
    NSLog(@"Subscriber 1 receive complete");
}];
[subject sendNext:@1];
[subject sendNext:@2];
[subject sendCompleted];

延时操作和Block生命周期
NSString *someStr = @"someStr"; 
[[RACScheduler scheduler] afterDelay:1 schedule:^{
    NSLog(@"%@", someStr);
}];

//mainThreadScheduler:延时操作和Block生命周期
NSString *someStr = @"someStr";
[[RACScheduler mainThreadScheduler] afterDelay:1 schedule:^{
    NSLog(@"%@", someStr);
}];

延时操作和Block生命周期
NSString *someStr = @"someStr";
NSString *someOtherStr = @"someOtherStr";
[RACScheduler.mainThreadScheduler afterDelay:1 schedule:^{
    NSLog(@"%@", someStr);
    NSString *innerStr = @"innerStr";
    [RACScheduler.mainThreadScheduler afterDelay:1 schedule:^{
        NSLog(@"%@ && %@", someOtherStr, innerStr);
    }];
}];

//afterDelay:延时订阅冷信号的情况
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    [subscriber sendNext:@1];
    [subscriber sendNext:@2];
    [subscriber sendCompleted];
    return nil;
}];
[signal subscribeNext:^(id x) {
    NSLog(@"Subscriber 1 receive next: %@", x);
} error:^(NSError *error) {
    NSLog(@"Subscriber 1 receive error: %@", error);
} completed:^{
    NSLog(@"Subscriber 1 receive complete");
}];

[RACScheduler.mainThreadScheduler afterDelay:1 schedule:^{
    [signal subscribeNext:^(id x) {
        NSLog(@"Subscriber 2 receive next: %@", x);
    } error:^(NSError *error) {
        NSLog(@"Subscriber 2 receive error: %@", error);
    } completed:^{
        NSLog(@"Subscriber 2 receive complete");
    }];
}];

//afterDelay:延时订阅热信号的情况
RACSubject *subject = [RACReplaySubject subject];
[subject subscribeNext:^(id x) {
    NSLog(@"Subscriber 1 receive next: %@", x);
} error:^(NSError *error) {
    NSLog(@"Subscriber 1 receive error: %@", error);
} completed:^{
    NSLog(@"Subscriber 1 receive complete");
}];

[RACScheduler.mainThreadScheduler afterDelay:1 schedule:^{
    [subject subscribeNext:^(id x) {
        NSLog(@"Subscriber 2 receive next: %@", x);
    } error:^(NSError *error) {
        NSLog(@"Subscriber 2 receive error: %@", error);
    } completed:^{
        NSLog(@"Subscriber 2 receive complete");
    }];
}];
[subject sendNext:@1];
[subject sendNext:@2];
[subject sendCompleted];


//afterDelay:冷信号延时发送
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    [RACScheduler.mainThreadScheduler afterDelay:1 schedule:^{
        [subscriber sendNext:@1];
        [subscriber sendNext:@2];
    }];
    [RACScheduler.mainThreadScheduler afterDelay:2 schedule:^{
        [subscriber sendCompleted];
    }];
    return nil;
}]; 
[signal subscribeNext:^(id x) {
    NSLog(@"Subscriber 1 receive next: %@", x);
} error:^(NSError *error) {
    NSLog(@"Subscriber 1 receive error: %@", error);
} completed:^{
    NSLog(@"Subscriber 1 receive complete");
}];

//afterDelay:热信号延时发送
RACSubject *subject = [RACSubject subject];
[subject subscribeNext:^(id x) {
    NSLog(@"Subscriber 1 receive next: %@", x);
} error:^(NSError *error) {
    NSLog(@"Subscriber 1 receive error: %@", error);
} completed:^{
    NSLog(@"Subscriber 1 receive complete");
}];
[RACScheduler.mainThreadScheduler afterDelay:1 schedule:^{
    [subject sendNext:@1];
    [subject sendNext:@2];
    [subject sendCompleted];
}];

//信号变换的生命周期
- (RACSignal *)myMap_:(id (^)(id))map
{
    NSCParameterAssert(map != nil);
    return [RACSignal createSignal:^RACDisposable *(id subscriber) {
        [self subscribeNext:^(id x) {
            [subscriber sendNext:map(x)];
        } error:^(NSError *error) {
            [subscriber sendError:error];
        } completed:^{
            [subscriber sendCompleted];
        }];
        return nil;
    }];
}

- (RACSignal *)myMap2_:(id (^)(id))map
{
    @weakify(self)
    NSCParameterAssert(map != nil);
    return [RACSignal createSignal:^RACDisposable *(id subscriber) {
        @strongify(self)
        [self subscribeNext:^(id x) {
            [subscriber sendNext:map(x)];
        } error:^(NSError *error) {
            [subscriber sendError:error];
        } completed:^{
            [subscriber sendCompleted];
        }];
        return nil;
    }];
}

- (RACSignal *)myMap_:(id (^)(id))map
{
    [self subscribeNext:^(id x) { 
    NSCParameterAssert(map != nil);
    return [RACSignal createSignal:^RACDisposable *(id subscriber) {
        [subscriber sendNext:map(x)];
    } error:^(NSError *error) {
        [subscriber sendError:error];
    } completed:^{
        [subscriber sendCompleted];
    }];
        return nil;
  }];
}

void signalTransform()
{
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id s) {
        [s sendNext:@1];
        [RACScheduler.mainThreadScheduler afterDelay:1 schedule:^{
            [s sendCompleted];
        }];
        return nil;
    }];
    RACSignal *signal2 = [signal myMap_:^id(NSNumber *value) {
        return @(value.integerValue * 2);
    }];

    [RACScheduler.mainThreadScheduler afterDelay:1 schedule:^{
        [signal2 subscribeNext:^(id x) {
            NSLog(@"Subscriber 1 receive next: %@", x);
        } error:^(NSError *error) {
            NSLog(@"Subscriber 1 receive error: %@", error);
        } completed:^{
            NSLog(@"Subscriber 1 receive complete");
        }];
    }];
}

//信号组合时的生命周期
+ (RACSignal *)myMerge_:(id)signals
{
    return [RACSignal createSignal:^RACDisposable *(id subscriber) {
        for (RACSignal *signal in signals) {
            [signal subscribeNext:^(id x) {
                [subscriber sendNext:x];
            } error:^(NSError *error) {
                [subscriber sendError:error];
            } completed:^{
                [subscriber sendCompleted];
            }];
        }
        return nil;
    }];
}


//RACDisposable对象断开订阅连接和RACScheduler对象(销毁信号)
@interface RACDisposable : NSObject
@property (atomic, assign, getter = isDisposed, readonly) BOOL disposed;
+ (instancetype)disposableWithBlock:(void (^)(void))block;
- (void)dispose;
@end

//RACDisposable:生命周期
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    [subscriber sendNext:@1];
    [RACScheduler.mainThreadScheduler afterDelay:2 schedule:^{
        [subscriber sendNext:@2];
        [subscriber sendCompleted];
    }];
    return [RACDisposable disposableWithBlock:^{
        NSLog(@"dispose!!!");
    }];
}];

RACDisposable *disposable = [signal subscribeNext:^(id x) {
    NSLog(@"next: %@", x);
} completed:^{
    NSLog(@"complete");
}];

[RACScheduler.mainThreadScheduler afterDelay:1 schedule:^{
    [disposable dispose];
}];

//RACDisposable:block的作用
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    [subscriber sendNext:@1];
    RACDisposable *disposable1 = [RACScheduler.mainThreadScheduler afterDelay:2 schedule:^{
        [subscriber sendNext:@2];
    }];
    RACDisposable *disposable2 = [RACScheduler.mainThreadScheduler afterDelay:2 schedule:^{
        [subscriber sendCompleted];
    }];
    return [RACDisposable disposableWithBlock:^{
        [disposable1 dispose];
        [disposable2 dispose];
        NSLog(@"dispose!!!");
    }];
}];

RACDisposable *disposable = [signal subscribeNext:^(id x) {
    NSLog(@"next: %@", x);
} completed:^{
    NSLog(@"complete");
}];

[RACScheduler.mainThreadScheduler afterDelay:1 schedule:^{
    [disposable dispose];
}];

//RACDisposable家族成员一:RACScopedDisposable绑定对象生存期的disposable

+ (instancetype)scopedDisposableWithDisposable:(RACDisposable *)disposable;
- (RACScopedDisposable *)asScopedDisposable;

RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    [subscriber sendNext:@1];
    RACDisposable *disposable1 = [RACScheduler.mainThreadScheduler afterDelay:2 schedule:^{
        [subscriber sendNext:@2];
    }];
    RACDisposable *disposable2 = [RACScheduler.mainThreadScheduler afterDelay:2 schedule:^{
        [subscriber sendCompleted];
    }];
    return [RACDisposable disposableWithBlock:^{
        [disposable1 dispose];
        [disposable2 dispose];
        NSLog(@"dispose!!!");
    }];
}];
RACDisposable *disposable = [signal subscribeNext:^(id x) {
    NSLog(@"next: %@", x);
} completed:^{
    NSLog(@"complete");
}];
self.someProp = disposable.asScopedDisposable;

//RACDisposable家族成员一:RACSerialDisposable可替换的disposable包裹

+ (instancetype)serialDisposableWithDisposable:(RACDisposable *)disposable;
@property (atomic, strong) RACDisposable *disposable;

- (RACSignal *)myConcat:(RACSignal *)signal
{
    return [RACSignal createSignal:^RACDisposable *(id subscriber) {
    RACSerialDisposable *mainDisposable = [[RACSerialDisposable alloc] init];
    mainDisposable.disposable = [self subscribeNext:^(id x) {
            [subscriber sendNext:x];
        } error:^(NSError *error) {
            [subscriber sendError:error];
        } completed:^{
            mainDisposable.disposable = [signal subscribe:subscriber];
        }];
        return mainDisposable;
    }];
}

家族成员一:RACSerialDisposable多个disposable的包裹

- (void)addDisposable:(RACDisposable *)disposable;
- (void)removeDisposable:(RACDisposable *)disposable;

+ (RACSignal *)myMerge:(id)signals
{
    return [RACSignal createSignal:^RACDisposable *(id subscriber) {
        RACCompoundDisposable *mainDisposable = [[RACCompoundDisposable alloc] init];
        for (RACSignal *signal in signals) {
            [mainDisposable addDisposable:[signal subscribeNext:^(id x) {
                [subscriber sendNext:x];
            } error:^(NSError *error) {
                [subscriber sendError:error];
                [mainDisposable dispose];
            } completed:^{
                [subscriber sendCompleted];
                [mainDisposable dispose];
            }]];
        }
        return mainDisposable;
    }];
}

ReactiveCocoa之实战演练
FRP概论!简单变换、组合高阶函数!冷热信号!并发编程!生命周期与订阅销毁!RACChannel!
RACCommand作用?
重复执行一个过程!开关控制!防重入!错误异常的处理!结果统一处理!
RACChannel作用?
双向通道!类似通话!处理回声问题!
RAC的循环引用问题?
RAC中如何识别判断循环引用!如何解决循环引用!破除循环引用的一端!避免构造循环引用
常见情况分析?
strong指针可能会引起循环引!weak指针不会引起循环引!block持有self != 循环引!block对捕获的strong指针会进行引用!block对捕获的weak指针不会进行引用
Weakify & Strongify的使用说明?1、必须成对使用否则无效2、不只是用于self,也可用于其他变量3、必须在使用前@strongify,否则无效4、可以同时处理多个对象@weakify(a, b, c)
循环引用的多种情况解析?1、信号创建时引起的循环引2、信号订阅时引起的循环引3、block嵌套时加几个strongify
RAC宏的使用?
RAC(self, a.b) vs RAC(self.a, b)的区别?1、被绑定的对象2、间接属性变化3、建议使用RAC(self, a.b)
取消RAC的宏绑定?1、重复绑定会抛异常2、takeUntil取消绑定3、使用展开式,调用方法
RACObserve(self, a.b) vs RACObserve(self.a, b)1、被监听的对象2、间接属性变化3、建议使用RACObserve(self, a.b)
适合RAC的情景?1、异步场景,异步处理需求、处理成功subscribeNext、处理异常和错误subscribeError 2、事件处理。UI事件、Delegate 3、切面处理。KVO
什么样的功能适合RAC!让OOP与FRP配合使!从FRP的叫度来分析问题!RAC使用小技巧

你可能感兴趣的:(ReactiveCocoa综合)