简介
RAC是函数式+响应式编程(FRP)结合,git仓库:https://github.com/ReactiveCocoa/ReactiveCocoa
它由四大核心组件构成:
- 信号源:RACStream 及其子类;
- 订阅者:RACSubscriber 的实现类及其子类;
- 调度器:RACScheduler 及其子类;
- 清洁工:RACDisposable 及其子类。
详细介绍可以前往ReactiveCocoa v2.5 源码解析之架构总览
RAC基本使用
示例:
代理
1
2
3
4
5
6
7
8
@weakify(self);
[[self rac_signalForSelector:@selector(tableView:didSelectRowAtIndexPath:)
fromProtocol:@protocol(UITableViewDelegate)] subscribeNext:^(RACTuple *tuple) {
NSIndexPath *indexPath = tuple.last;
@strongify(self);
[[NSNotificationCenter defaultCenter] postNotificationName:RACTestNotification object:indexPath];
self.selectIndex = indexPath.row;
}];
target-action
1
2
3
4
5
6
7
8
9
10
11
12
UIButton *testButton = ({
UIButton *button = [UIButton buttonWithType:0];
button.frame = CGRectMake(100, 50, 100, 30);
[button setTitle:@"点击" forState:UIControlStateNormal];
[button setBackgroundColor:UIColor.redColor];
button;
});
[self.view addSubview:testButton];
[[testButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"点击:%@", x);
}];
通知,内部在订阅结束后移除观察者,所以不需要手动移除观察者
1
2
3
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:RACTestNotification object:nil] subscribeNext:^(NSNotification *noti) {
NSLog(@"%@", noti.object);
}];
KVO,其内部使用RACKVOTrampoline(RACDisposable子类)会自动维护监听者的释放,也不需要移除
1
2
3
4
// 警告fix: pod 'ReactiveCocoa', :git => 'https://github.com/zhao0/ReactiveCocoa.git', :tag => '2.5.2'
[RACObserve(self, selectIndex) subscribeNext:^(id x) {
NSLog(@"%tu", [x integerValue]);
}];
组合
1
2
3
4
5
6
7
8
9
10
11
12
[[[RACSignal
combineLatest:@[ RACObserve(self, selectIndex), RACObserve(self, title) ]
reduce:^(NSNumber *selectIndex, NSString *title) {
return @(selectIndex.integerValue > 0 && title.length);
}] distinctUntilChanged]
subscribeNext:^(NSNumber *valid) {
if (valid.boolValue) {
NSLog(@"符合条件");
} else {
NSLog(@"不符合条件");
}
}];
映射
1
2
3
4
5
6
7
8
RACSubject *subject = [RACSubject subject];
RACSignal *signal = [subject map:^id(id value) {
return [value stringByAppendingString:@"_hello"];
}];
[signal subscribeNext:^(id x) {
NSLog(@"%s, %@", __func__, x);
}];
[subject sendNext:@"hi"];
绑定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
RACSubject *subject = [RACSubject subject];
RACSignal *bindSignal = [subject bind:^RACStreamBindBlock{
// block 绑定信号就会调用 此处一般不做处理
return ^RACSignal *(id value, BOOL *stop) {
// 处理一般在此处开始
// (源信号)绑定信号发送新值时调用
NSLog(@"接收到的内容:%@", value);
// *stop = YES 本次订阅后终止
if ([value isEqualToString:@"123"]) {
*stop = YES;
}
// 返回信号 或者empty
return [RACReturnSignal return:[value stringByAppendingString:@"_hello"]];
};
}];
[bindSignal subscribeNext:^(id x) {
// 改变后的值
NSLog(@"%s, %@", __func__, x);
}];
[subject sendNext:@"abc"];
[subject sendNext:@"123"];
[subject sendNext:@"456"];
跳跃
1
2
3
4
5
6
7
8
// 跳跃过前面n个值
RACSubject *subject = [RACSubject subject];
[[subject skip:1] subscribeNext:^(id x) {
NSLog(@"%s, %@", __func__, x);
}];
[subject sendNext:@1];
[subject sendNext:@2];
[subject sendNext:@3];
take
1
2
3
4
5
6
7
RACSubject *subject = [RACSubject subject];
[[subject take:2] subscribeNext:^(id x) {
NSLog(@"%s, %@", __func__, x);
}];
[subject sendNext:@1];
[subject sendNext:@2];
[subject sendNext:@3];
元组
1
2
3
4
5
6
7
8
// 包装元组
RACTuple *tuple = RACTuplePack(@1, @2, @3);
NSLog(@"%s, %@", __func__, tuple);
// 解包
RACTuple *tuple = RACTuplePack(@1, @2);
RACTupleUnpack(NSNumber * number1, NSNumber * number2) = tuple;
NSLog(@"%s, %@, %@", __func__, number1, number2);
解决循环引用@weakify(self)和@strongify(self)
1
2
3
4
5
6
@weakify(self);
[[self rac_signalForSelector:@selector(tableView:didSelectRowAtIndexPath:) fromProtocol:@protocol(UITableViewDelegate)] subscribeNext:^(RACTuple *tuple) {
@strongify(self);
NSIndexPath *indexaPath = tuple.last;
self.selectIndex = indexaPath.row;
}];
冷信号转换为热信号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 冷信号转换为热信号 适用场景:多个订阅者订阅一个信号, 不需要发送多个消息
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 不管有几个订阅者, 只发送一次
NSLog(@"发送消息");
[subscriber sendNext:@"abc"];
[subscriber sendCompleted];
return nil;
}];
// 创建连接类
RACMulticastConnection *connection = [signal publish];
[connection.signal subscribeNext:^(id x) {
NSLog(@"订阅者1: %@", x);
}];
[connection.signal subscribeNext:^(id x) {
NSLog(@"订阅者2: %@", x);
}];
[connection.signal subscribeNext:^(id x) {
NSLog(@"订阅者3: %@", x);
}];
// 创建连接, 此过程将会把冷信号转化为热信号
[connection connect];
RACSignal用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"abc"];
// 必须调用 或者调用sendError
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
// 信号取消订阅时调用
NSLog(@"%s, 订阅取消", __func__);
}];
}];
// 订阅后返回取消订阅信号
RACDisposable *disposable = [signal subscribeNext:^(id x) {
NSLog(@"%s, %@", __func__, x);
}];
// 取消订阅
[disposable dispose];
RACSubject用法
1
2
3
4
5
6
7
8
9
RACSubject *subject = [RACSubject subject];
// 必须先订阅, 才能发送
[subject sendNext:@"abc"];
// 指明生命周期
[[subject takeUntil:self.rac_willDeallocSignal] subscribeNext:^(id x) {
NSLog(@"%s, %@", __func__, x);
}];
[subject sendNext:@"123"];
RACCommand用法
1
2
3
4
5
6
7
// RACCommand
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
return dispose;
}];
}];
[command execute:nil];
RACSequence用法
1
2
3
4
5
6
7
8
// RACSequence
NSArray *strings = @[ @"as", @"safaf", @"sfasdfds", @"dfdsg" ];
NSArray *results = [[[strings.rac_sequence filter:^BOOL(NSString *str) {
return str.length > 2;
}] map:^id(NSString *str) {
return [str stringByAppendingString:@"_hello"];
}] array];
NSLog(@"%@", results);
MVVM
MVVM由Microsoft架构师Ken Cooper和Ted Peters提出,用于简化用户界面的事件驱动编程。
在MVC中大致是这样的:
在MVVM是这样的:
使用MVVM的优点:
- 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变;
- 可重用性。可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑;
- 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面(View)设计,使用Expression Blend可以很容易设计界面并生成xaml代码;
- 可测试。针对ViewModel来对View写测试代码,会简单些,它能够减少Controller的复杂性,使得表示逻辑更易测试。