学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

ReactiveObjC基础

RAC 被描述为函数响应式编程

编程思想

  1. 函数式编程 :使用高阶函数,例如函数用其他函数作为参数。
  2. 响应式编程:关注于数据流和变化传播。
  3. 链式编程 : 是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好。a(1).b(2).c(3),注意点:要想达到链式编程方法的返回值必须是一个( (返回值是本身对象的)block)

入门

Signal 传递的 data

Signal 传递的 data 是 event,它所传递的 event 包括 3 种:值事件、完成事件和错误事件。在传递值事件时,可以携带数据.

传递值事件/完成事件/错误事件的本质就是向 sub scriber 发送sendNext:、sendComplete以及sendError:消息

Signal的简单使用

创建信号、订阅信号、订阅过程å

  • 创建获取信号
  1. 创建单元信号
  2. 创建动态信号
  3. 通过 Cocoa 桥接
  4. 从别的信号变换而来
  5. 由序列变换而来

Cocoa桥接

1
2
3
4
RACSignal *signal6 = [objectrac_signalForSelector:@selector(setFrame:)];
RACSignal *signal7 = [control rac_signalForControlEvents:UIControlEventTouchUpInside];
RACSignal *signal8 = [object rac_willDeallocSignal];
RACSignal *signal9 = RACObserve(object, keyPath);

信号变换

1
2
3
RACSignal *signal10 = [signal1 map:^id(id value) {
return someObject;
}];

序列变换

1
RACSignal *signal11 = sequence.signal;
  • 订阅信号

订阅信号的方式有 3 种:

  1. 通过subscribeNext:error:completed:方法订阅
  2. RAC 宏绑定
  3. Cocoa 桥接

通过subscribeNext:error:completed:方法订阅

1
2
3
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
error:(void (^)(NSError *error))errorBlock
completed:(void (^)(void));

RAC 宏绑定

1
RAC(view, backgroundColor) = signal10;

Cocoa 桥接

1
2
3
[object rac_liftSelector:@selector(someSelector:) withSignals:signal1, signal2, nil];
[object rac_liftSelector:@selector(someSelector:) withSignalsFromArray:@[signal1, signal2]];
[object rac_liftSelector:@selector(someSelector:) withSignalOfArguments:signal1];
  • 订阅过程
    订阅过程指的是信号被订阅的处理逻辑
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
  
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"1"];
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
[subscriber sendNext:@"3"]; // 无效
return [RACDisposable disposableWithBlock:^{
NSLog(@"dispose"); // 当错误事件或者完成事件产生时,该block被调用
}];
}];

[signal subscribeNext:^(id x) {
NSLog(@"next value is : %@", x);
} error:^(NSError *error) {
NSLog(@"error : %@", error);
} completed:^{
NSLog(@"completed");
}];

/* prints:
next value is : 1
next value is : 2
completed
dispose
*/
  • 注意:
  1. RACSignal的每一个操作都会返回一个RACSignal。
  2. RACSequence是RAC中的集合类,可以实现OC对象与信号中传递值之间的转换,RAC类库中提供了NSArray,NSDictionary等集合类的分类供其转换

可能遇到的坑

接收不到信号

1
2
3
4
5
6
7
8
9
[RACObserve(self, selectedRows) subscribeNext:^(NSSet *currentlySelected) {
NSLog(@"Currently selected: %@", currentlySelected);
}];

self.selectedRows = [NSMutableSet set];
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.selectedRows addObject:indexPath];
}

上面RACObserve回调只会调用一次,初始化的时候,为什么addObject的时候没有出发信号?
因为addObject没有触发KVO事件。解决通过协议触发KVO

1
2
3
4
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[[self mutableSetValueForKey:@"selectedRows"] addObject:indexPath];
}

需要关联多个信号

1
RAC(self.loginVM,pwdStr)=[RACSignal merge:@[passwordTxt.rac_textSignal,RACObserve(passwordTxt, text)]];

冷信号和热信号

冷热信号的概念源于.NET框架Reactive Extensions(RX)中的Hot Observable和Cold Observable,两者的区别是:

Hot Observable是主动的,尽管你并没有订阅事件,但是它会时刻推送,就像鼠标移动;而Cold Observable是被动的,只有当你订阅的时候,它才会发布消息。

Hot Observable可以有多个订阅者,是一对多,集合可以与订阅者共享信息;而Cold Observable只能一对一,当有不同的订阅者,消息是重新完整发送。

  • 冷信号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-(void)racColdSingle
{
RACSignal* coldSingle=[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@1];
[subscriber sendNext:@2];
[subscriber sendNext:@3];
[subscriber sendCompleted];
return nil;
}];

NSLog(@"Signal was created.");
[[RACScheduler mainThreadScheduler] afterDelay:0.1 schedule:^{
[coldSingle subscribeNext:^(id x) {
NSLog(@"Subscriber 1 recveive: %@", x);
}];
}];

[[RACScheduler mainThreadScheduler] afterDelay:1 schedule:^{
[coldSingle subscribeNext:^(id x) {
NSLog(@"Subscriber 2 recveive: %@", x);
}];
}];
}
1
2
3
4
5
6
7
2019-09-23 14:14:26.853285+0800 RACDemo[16585:117906] Signal was created.
2019-09-23 14:14:26.956126+0800 RACDemo[16585:117906] Subscriber 1 recveive: 1
2019-09-23 14:14:26.956339+0800 RACDemo[16585:117906] Subscriber 1 recveive: 2
2019-09-23 14:14:26.956559+0800 RACDemo[16585:117906] Subscriber 1 recveive: 3
2019-09-23 14:14:27.888112+0800 RACDemo[16585:117906] Subscriber 2 recveive: 1
2019-09-23 14:14:27.888267+0800 RACDemo[16585:117906] Subscriber 2 recveive: 2
2019-09-23 14:14:27.888363+0800 RACDemo[16585:117906] Subscriber 2 recveive: 3

信号在14:14:26.853时被创建,14:14:26.956依次接到1、2、3三个值,而在14:14:27.888再依次接到1、2、3三个值。说明了变量名为coldSingle的这个信号,在两个不同时间段的订阅过程中,分别完整地发送了所有的消息。

  • 热信号
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
35
36
37
-(void)racHoltSingal
{
RACMulticastConnection *connection = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[RACScheduler mainThreadScheduler] afterDelay:1 schedule:^{
[subscriber sendNext:@1];
}];

[[RACScheduler mainThreadScheduler] afterDelay:2 schedule:^{
[subscriber sendNext:@2];
}];

[[RACScheduler mainThreadScheduler] afterDelay:3 schedule:^{
[subscriber sendNext:@3];
}];

[[RACScheduler mainThreadScheduler] afterDelay:4 schedule:^{
[subscriber sendCompleted];
}];
return nil;
}] publish];

[connection connect];
RACSignal *signal = connection.signal;

NSLog(@"Signal was created.");
[[RACScheduler mainThreadScheduler] afterDelay:1.1 schedule:^{
[signal subscribeNext:^(id x) {
NSLog(@"Subscriber 1 recveive: %@", x);
}];
}];

[[RACScheduler mainThreadScheduler] afterDelay:2.1 schedule:^{
[signal subscribeNext:^(id x) {
NSLog(@"Subscriber 2 recveive: %@", x);
}];
}];
}
1
2
3
4
2019-09-23 14:20:26.214718+0800 RACDemo[16872:122429] Signal was created.
2019-09-23 14:20:28.325190+0800 RACDemo[16872:122429] Subscriber 1 recveive: 2
2019-09-23 14:20:29.274356+0800 RACDemo[16872:122429] Subscriber 1 recveive: 3
2019-09-23 14:20:29.274537+0800 RACDemo[16872:122429] Subscriber 2 recveive: 3

分析

  1. 创建了一个信号,在1秒、2秒、3秒分别发送1、2、3这三个值,4秒发送结束信号。
  2. 对这个信号调用publish方法得到一个RACMulticastConnection。
  3. 让connection进行连接操作。
  4. 获得connection的信号。
  5. 分别在1.1秒和2.1秒订阅获得的信号。
  • 热信号的如下特点:
  1. 热信号是主动的,即使你没有订阅事件,它仍然会时刻推送。如第二个例子,信号在50秒被创建,51秒的时候1这个值就推送出来了,但是当时还没有订阅者。而冷信号是被动的,只有当你订阅的时候,它才会发送消息。如第一个例子。
  2. 热信号可以有多个订阅者,是一对多,信号可以与订阅者共享信息。如第二个例子,订阅者1和订阅者2是共享的,他们都能在同一时间接收到3这个值。而冷信号只能一对一,当有不同的订阅者,消息会从新完整发送。如第一个例子,我们可以观察到两个订阅者没有联系,都是基于各自的订阅时间开始接收消息的。
  • 热信号几种产生方式
  1. [RACSignal publish]、- [RACMulticastConnection connect]、- [RACMulticastConnection signal]这几个操作生成了一个热信号

  2. 在RAC中,所有的热信号都属于一个类RACSubject

  • 冷信号转化成热信号—广播

冷信号与热信号的本质区别在于是否保持状态,冷信号的多次订阅是不保持状态的,而热信号的多次订阅可以保持状态,所以一种将冷信号转换为热信号的方法就是,将冷信号订阅,订阅到的每一个时间通过RACSubject发送出去,其它订阅者只订阅这个RACSubject

参考

  1. (RAC)知其所以然(源码分析)
  2. 美团ReactiveCocoa