RAC-学习笔记01

文章目录
  1. 1. 学习目的
  2. 2. 主要概念
  3. 3. 如何创建信号
    1. 3.1. 单元信号
    2. 3.2. 动态信号
    3. 3.3. Cocoa 桥接
    4. 3.4. 信号变化(内部是产生一个新信号的)
    5. 3.5. 序列转换
  4. 4. 订阅(绑定)信号的方式
    1. 4.1. 直接订阅方法
    2. 4.2. 绑定
    3. 4.3. Cocoa 桥接
  5. 5. 信号变化&组合
    1. 5.1. 值操作
    2. 5.2. 数量操作
    3. 5.3. 时间操作
    4. 5.4. 多个信号组合
    5. 5.5. 信号的高阶操作(升阶降阶)
  6. 6. 冷信号&热信号
  7. 7. 一些习题

学习目的

  1. 知道 rac 都有什么
  2. 知道他们都怎么用
  3. 知道为什么有这些东西
  4. 根类归纳整理
  5. 如何自己扩展
  6. 适当分析源码

主要概念

  1. 信号
  2. 信号变换&组合
  3. 订阅(绑定)
  4. 取消订阅

RACStream

RACSignal : 基于时间的异步事件流

RACSequence : 惰性集合,同步(很少用),collection类型的封装,可以使用 Stream(monad)封装的函数式方式

如何创建信号

单元信号

1
2
3
4
RACSignal *sig0 = [RACSignal error:NSError.new];
RACSignal *sig1 = [RACSignal return:@0];
RACSignal *sig2 = [RACSignal empty];
RACSignal *sig3 = [RACSignal never];

动态信号

1
2
3
4
5
6
7
RACSignal *s = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@0];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{

}];
}];

Cocoa 桥接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- (void) bridge {
RACSignal *sO = RACObserver(self, button);

self.sigSetFrame = [self.button rac_signalForSelector:@selector(setFrame:)];
[_sigSetFrame
subscribeNext:^(id x) {
NSLog(@"setFrame:%@", x);
}];

self.sigClick = [self.button rac_signalForControlEvents:UIControlEventTouchUpInside];
[_sigClick
subscribeNext:^(id x) {
NSLog(@"event: %@", x);
}];

[[self rac_liftSelector:@selector(lift:) withSignals:_sigClick, nil]
subscribeNext:^(id x) {
NSLog(@"%@", x);
}];

[[self rac_liftSelector:@selector(lift:) withSignalsFromArray:@[[_sigClick map:^id(id value) {
return @[@3];
}]]]
subscribeNext:^(id x) {
NSLog(@"%@", x);
}];

[self rac_liftSelector:@selector(lift:) withSignalOfArguments:[_sigClick mapReplace:RACTuplePack(@1)]];
}

- (int) lift:(id)value{
printf("lift: %s", __func__);
return 1;
}

信号变化(内部是产生一个新信号的)

1
2
3
[_sigClick map:^id(id value) {
return @[@3];
}];

序列转换

1
self.sigSequence = [[RACSequence return:@3] concat:[RACSequence return:@4]].signal;

订阅(绑定)信号的方式

直接订阅方法

1
2
3
4
[self.sigSample
subscribeNext:^(id x) {
NSLog(@"sample: %@", x);
}];

绑定

1
RAC(self, a) = Signal;

Cocoa 桥接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)signal {
[[self rac_liftSelector:@selector(lift:) withSignals:_sigClick, nil]
subscribeNext:^(id x) {
NSLog(@"%@", x);
}];

[[self rac_liftSelector:@selector(lift:) withSignalsFromArray:@[[_sigClick map:^id(id value) {
return @[@3];
}]]]
subscribeNext:^(id x) {
NSLog(@"%@", x);
}];

[self rac_liftSelector:@selector(lift:) withSignalOfArguments:[_sigClick mapReplace:RACTuplePack(@1)]];
}

- (int) lift:(id)value{
printf("lift: %s", __func__);
return 1;
}

信号变化&组合

  1. 单个信号的变化
  2. 多个信号的组合
  3. 高阶操作

signal 变化

值操作

问题:

  • 为什么会有这样的值操作方法?
  • 自己如何扩展新的值方法

  • transform 这些用的比较多
    1. map
    2. MapReplace
    3. ReduceEach tuple(a, b) -> c
  • 值判断逻辑变换
    1. not
    2. and
    3. or
  • 用的比较少
    1. reduceApply 这个不太清楚为什么要这么设计,用combineLatest: reduceEach: 就可以做了,而且代码看起来更好。
    2. materialize
    3. dematerialize

数量操作

  1. repeat 一直会有值
  • 条件过滤1

    1. ignore
    2. ignoreValues
    3. distinctUntilChanged
  • 条件过滤2

    1. takeUntilBlock:(BOOL (^)(id x))predicate
    2. takeWhileBlock:(BOOL (^)(id x))predicate;
    3. skipUntilBlock:(BOOL (^)(id x))predicate;
    4. skipWhileBlock:(BOOL (^)(id x))predicate;
  • 数量判断,如果有值就发送

    1. any;
    2. any:(BOOL (^)(id object))predicateBlock;
    3. all:(BOOL (^)(id object))predicateBlock;
  • 重试

    1. retry
    2. retry: Count
    3. collect 汇聚 信号必须有返回值

副作用:
– 对于信号值变化以外的一些操作

  • doNext
  • doCompleted
  • doError

折叠函数

不听对一个value 操作,使用折叠函数解决这个问题

1
2
3
[sig10 aggregateWithStart:@0 reduce:^id(id running, id next) {
return @([running intValue] + [next intValue]);
}];
  1. (RACSignal *)aggregateWithStart:(id)start reduce:(id (^)(id running, id next))reduceBlock;
  2. (RACSignal *)aggregateWithStart:(id)start reduceWithIndex:(id (^)(id running, id next, NSUInteger index))reduceBlock;
  3. (RACSignal *)aggregateWithStartFactory:(id (^)(void))startFactory reduce:(id (^)(id running, id next))reduceBlock;
  4. (instancetype)scanWithStart:(id)startingValue reduce:(id (^)(id running, id next))reduceBlock;
  5. (instancetype)scanWithStart:(id)startingValue reduceWithIndex:(id (^)(id running, id next, NSUInteger index))reduceBlock;

时间操作

  1. + (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler;
  2. + (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler withLeeway:(NSTimeInterval)leeway;
  3. delay
  4. throttle 阀门,在固定时间内没有新值发送的时候,会发送最后的值

多个信号组合

问题:

  1. 受哪个信号终止而终止?
  2. 错误传递?
  3. 各个信号何时开始开始订阅?
  4. 在哪个线程发出?
  • concat
    • 第一个结束后,订阅第二个
    • 第一个error 后,就直接 error
  • merge
  • zip
  • combineLatest
  • sample
  • takeUntil
  • takeUntilReplacement, 当 B 来了直接替换 A,开始订阅 B

信号的高阶操作(升阶降阶)

  1. 升阶 S(v) -> S(s(v))
  2. 降阶 S(s(v)) -> S(v)
1
2
3
4
RACSignal *signal = @[@1, @2, @3, @4].rac_sequence.signal;
RACSignal *signalB = [[signal map:^id(id value) {
return [[RACSignal return:value] delay:1];
}] concat];
  • 降阶操作

  • switchToLatests

switchToLatests

  • if/then/else
    if/then/else

  • switch/cases/default

  • flatten

flatten

flatten 类似 merge 只不过一个是接收的 value是 signal,另一个接收的就是 value

  • flatten:count 按个数展开信号,当信号个数 > count 以后等待,如果有 sig completed,那么把等待中的sig 放入展开数组里面

flatten-count

flatten:1 == concat

  • flattenMap

满足 monad 的部分定义,绝大部分函数都可以使用 flattenMap 实现

  • bind

大部分函数都可以使用 bind 实现

冷信号&热信号

一些习题

  1. 如何获得无限递增的信号
1
2
3
4
5
6
7
8
RACSignal *increment(int inc) {
RACSignal *repeat = [[RACSignal return:@(inc)] repeat];

return [[repeat scanWithStart:0 reduce:^id(id running, id next) {
return @([running intValue] + [next intValue]);
}]
delay:1];
}
  1. fibonacci
1
2
3
4
5
6
7
RACSignal *fibonacci() {
RACSignal *repeat = [[RACSignal return:nil] repeat];
return [repeat scanWithStart:RACTuplePack(@1, @1) reduce:^id(RACTuple *running, id _) {
int next = [running.first intValue] + [running.second intValue];
return RACTuplePack(running.second, @(next));
}];
}