RAC学习笔记

RAC简介

RAC要解决的问题是传统ios中事件,通知,回调的机制十分的繁琐。

常规用法

target-action

  • RAC可以替代一般的target-action操作,例如监测textField的文字的改变,不是用RAC的写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)viewDidLoad {
[super viewDidLoad];

UITextField *text = ({
UITextField *text = [[UITextField alloc] init];
[self.view addSubview:text];
[text mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.view);
make.size.mas_equalTo(CGSizeMake(200, 40));
}];
text.backgroundColor = [UIColor redColor];
[text addTarget:self action:@selector(textDidChange:) forControlEvents:UIControlEventEditingChanged];
text;
});
}

- (void)textDidChange:(UITextField *)sender {
NSLog(@"did change: %@", sender);
}
  • 使用RAC的写法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    - (void)viewDidLoad {
    [super viewDidLoad];

    UITextField *text = ({
    UITextField *text = [[UITextField alloc] init];
    [self.view addSubview:text];
    [text mas_makeConstraints:^(MASConstraintMaker *make) {
    make.center.equalTo(self.view);
    make.size.mas_equalTo(CGSizeMake(200, 40));
    }];
    text.backgroundColor = [UIColor redColor];

    [[text rac_signalForControlEvents:UIControlEventEditingChanged] subscribeNext:^(id x) {
    NSLog(@"%@", x);
    }];

    // 或者
    //[text.rac_textSignal subscribeNext:^(id x) {
    // NSLog(@"%@", x);
    //}];

    text;
    });
    }

手势中使用RAC

  • RAC可以替换手势的方法,代码如下
1
2
3
4
5
6
7
8
9
10
- (void)viewDidLoad {
[super viewDidLoad];

self.view.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
[[tap rac_gestureSignal] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
[self.view addGestureRecognizer:tap];
}

通知

  • RAC可以代替通知,并且RAC的通知不用移出
1
2
3
4
5
6
- (void)viewDidLoad {
[super viewDidLoad];
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIApplicationDidBecomeActiveNotification object:nil] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
}

定制器

  • RAC可以代替定时器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)viewDidLoad {
[super viewDidLoad];

// 延迟2秒执行
[[RACScheduler mainThreadScheduler] afterDelay:2.0 schedule:^{
NSLog(@"haha");
}];

// interval为间隔事件,scheduler为在那个线程上执行,leeway为多久之后开始执行。
[[RACSignal interval:2.0 onScheduler:[RACScheduler mainThreadScheduler] withLeeway:5.0] subscribeNext:^(NSDate x) {
NSLog(@"%@", x);
}];

}

代理

  • RAC可以替换代理,但仅限没有返回值的代理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@interface ViewController ()<UITableViewDelegate>
@property (nonatomic, strong) UIAlertView *alertView;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

self.alertView = [[UIAlertView alloc] initWithTitle:@"RAC" message:@"test" delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)]subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 或者
// [[self.alertView rac_buttonClickedSignal] subscribeNext:^(id x) {
// NSLog(@"%@", x);
// }];

[self.alertView show];
}

@end

KVO

  • RAC可以替换KVO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@interface ViewController ()

@property (nonatomic, assign) NSInteger index;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

[RACObserve(self, index) subscribeNext:^(id x) {
NSLog(@"%@", x);
}];

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.index++;
}

@end

高级用法

创建信号

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
- (void)viewDidLoad {
[super viewDidLoad];

RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
[NSURLConnection sendAsynchronousRequest:req queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
if (connectionError) {
// 请求失败
[subscriber sendError:connectionError];
}
else {
// 请求成功
[subscriber sendNext:data];
}
[subscriber sendCompleted];
}];
return [RACDisposable disposableWithBlock:^{
// 取消订阅
NSLog(@"cancel");
}];
}];

RACDisposable *disposable = [signal subscribeNext:^(NSData *responseData) {
NSLog(@"%@", responseData);
} error:^(NSError *error) {
NSLog(@"%@", error);
} completed:^{
NSLog(@"completed");
}];

// 订阅完就取消订阅,这样就不会执行订阅block了
[disposable dispose];
}

信号处理

  • map:映射
  • filter:过滤
  • delay:延时
  • startWith:在接收到信号前,先接收到一个信号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)viewDidLoad {
[super viewDidLoad];

UITextField *textField = ({
UITextField *view = [[UITextField alloc] init];
[self.view addSubview:view];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.view);
make.size.mas_equalTo(CGSizeMake(200, 40));
}];
view.backgroundColor = [UIColor redColor];
view;
});

[[[[[[textField rac_textSignal] map:^id(id value) {
NSString *str = (NSString *)value;
return @([str length]);
}] filter:^BOOL(id value) {
return [value integerValue] > 3;
}] delay:2] startWith:@"haha"] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
}
  • timeout:超时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

[[RACScheduler mainThreadScheduler] afterDelay:1.0 schedule:^{
[subscriber sendNext:@"hello"];
[subscriber sendCompleted];
}];
return nil;

}] timeout:2.0 onScheduler:[RACScheduler mainThreadScheduler]]; // 如果请求没有在2秒内完成,则请求超时,会调用errorBlock

[signal subscribeNext:^(NSData *responseData) {
NSLog(@"%@", responseData);
} error:^(NSError *error) {
NSLog(@"%@", error);
} completed:^{
NSLog(@"completed");
}];
  • take:接收到多次信号,但只处理前几次信号
  • takeLast:只处理最后几次信号
  • skip:忽略几次
  • takeUntilBlock:返回YES可以表示不接收,返回NO表示接收该信号,与filter类似
  • takeWhileBlock:当返回一次YES后,就停止接收所有信号
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:@"hello1"];
[subscriber sendNext:@"hello2"];
[subscriber sendNext:@"hello3"];
[subscriber sendNext:@"hello4"];
[subscriber sendNext:@"hello5"];
[subscriber sendCompleted];
return nil;

}] takeLast:2];

[signal subscribeNext:^(id value) {
NSLog(@"%@", value);
}];
  • throttle:节流,可以规定两个信号的间隔时间必须大于某个值
  • distinctUntilChanged:直到订阅的内容发生变化才会处理信号
  • ignore:忽略某些信号值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
UITextField *textField = ({
UITextField *view = [[UITextField alloc] init];
[self.view addSubview:view];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.view);
make.size.mas_equalTo(CGSizeMake(200, 40));
}];
view.backgroundColor = [UIColor redColor];
view;
});

// 如果文字的改变速度过快,则不处理,两次信号间隔时间必须大于0.3秒
[[[[[textField rac_textSignal] throttle:0.3] distinctUntilChanged] ignore:@""] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
  • flattenMap:订阅的值为一个信号,将该信号转为订阅具体的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
UITextField *textField = ({
UITextField *view = [[UITextField alloc] init];
[self.view addSubview:view];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.view);
make.size.mas_equalTo(CGSizeMake(200, 40));
}];
view.backgroundColor = [UIColor redColor];
view;
});

[[[textField.rac_textSignal throttle:0.3] flattenMap:^id(id value) {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"aaa"];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{

}];
}];
}] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
  • repeat:重复发送某个信号
1
2
3
4
5
6
7
8
9
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:[NSDate date]];
[subscriber sendCompleted];
return nil;
}] repeat];

[signal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
  • merge:同时订阅两个信号源,无论哪个发出信号,都会执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subscriber sendNext:@"1"];
[subscriber sendCompleted];
});
return nil;
}];

RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
});
return nil;
}];

[[signalA merge:signalB] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
  • combineLatest:reduce:这是一个RACSignal的类方法,表示同时聚合多个信号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subscriber sendNext:@"1"];
[subscriber sendCompleted];
});
return nil;
}];

RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
});
return nil;
}];

[[RACSignal combineLatest:@[signalA, signalB] reduce:^id(NSString *s1, NSString *s2){
NSLog(@"s1=%@, s2=%@", s1, s2);
return [NSString stringWithFormat:@"%@%@", s1, s2];
}] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
  • concat:先订阅信号A,A执行完成后再订阅信号B
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subscriber sendNext:@"1"];
[subscriber sendCompleted];
});
return nil;
}];

RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
});
return nil;
}];

// 先订阅A,A执行完成后,再执行B,如果A失败了,不会再执行B
[[signalA concat:signalB] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
  • zipWith:两个信号都发出后,再执行,也可以使用combineLatest:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subscriber sendNext:@"1"];
[subscriber sendCompleted];
});
return nil;
}];

RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
});
return nil;
}];

[[signalA zipWith:signalB] subscribeNext:^(id x) {
// x是一个RACTuple,包含了signalA和signalB的值
NSLog(@"%@", x);
}];

RAC的坑

  • side effect: 一个信号源被多个订阅者订阅,每个订阅者收到的信号可能不一样。解决方法:使用reply
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
RACSignal *signalA = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
static int i = 0;
i++;
[subscriber sendNext:@(i)];
[subscriber sendCompleted];
return nil;
}] replay];

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

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