RACSignal
RAC中统一的数据接口,控件的事件,包括KVO,timer都可以转化成RACSignal。
创建:
1.RAC未控件的一部分原来的事件都通过Category的方式定义了event对应的signal,只需要直接拿来使用就好了。
2.自己创建
1 2
| + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber)) didSubscribe;
|
3.订阅
1 2 3
| [self.usernameTextField.rac_textSignal subscribeNext:^(id x) { NSLog(@"%@", x); }];
|
在信号创建的时候,需要传入一个didSubscribe的block,在有其他订阅者订阅这个信号的时候,didSubscribe就会被调用,然后将数据通过subscribeNext的block传入。
信号事件的种类
1.next,一般情况下,信号处理业务逻辑正常返回的时候,会调用订阅者的sendNext方法将数据传入订阅者,订阅者可以通过过subscribeNext获取数据。
2.error,有时候业务逻辑产生异常的时候,会调用订阅者的sendError方法来告知订阅者产生了异常,订阅者在subscribeNext:erro:中处理异常错误。
3.completed,该事件表示订阅者从信号中移除,之后不再收到消息了,信号生命周期结束。
创建一个请求账号权限的信号
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
| - (RACSignal *)requestAccessToTwitterSignal { // 1 - define an error NSError *accessError = [NSError errorWithDomain:RWTwitterInstantDomain code: RWTwitterInstantErrorAccessDenied userInfo:nil]; // 2 - create the signal @weakify(self) return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { // 3 - request access to twitter @strongify(self) [self.accountStore requestAccessToAccountsWithType:self.twitterAccountType options:nil completion:^(BOOL granted, NSError *error) { // 4 - handle the response if (!granted) { [subscriber sendError:accessError]; } else { [subscriber sendNext:nil]; [subscriber sendCompleted]; } }]; return nil; }]; }
|
处理请求账号权限的信号
1 2 3 4 5 6
| [[self requestAccessToTwitterSignal] subscribeNext:^(id x) { NSLog(@"Access granted"); } error:^(NSError *error) { NSLog(@"An error occurred: %@", error); }];
|
信号的几种操作
filter
将信号过滤,只保留满足block中条件的信号
只保留输入字符串长度大于3的信号
1 2 3 4 5 6 7 8 9
| [[self.usernameTextField.rac_textSignal filter:^BOOL(id value) { NSString *text = value; return text.length > 3; }] subscribeNext:^(id x) { //这里只会显示长度大于3的字符串 NSLog(@"%@", x); }];
|
这里的信号先经过filter过滤了一次,再由subscribeNext得到过滤的信号进行处理,其数据流如下图所示:
map
把源信号的值映射成一个新的值
1 2 3 4 5 6 7 8 9 10
| [[[self.usernameTextField.rac_textSignal map:^id(NSString *text) { return @(text.length); }] filter:^BOOL(NSNumber *length) { return [length integerValue] > 3; }] subscribeNext:^(id x) { NSLog(@"%@", x); }];
|
信号先经过map映射将字符串转化成功了字符串长度的数据,再通过filter过滤掉了字符串长度等于3的信号,最后通过subscribeNext获得最后符合条件的信号,整个过程包含了不同数据类型的变化,过程如下图:
http://7xqgnx.com1.z0.glb.clouddn.com/RAC2.png
combineLatest
将多个信号合并起来,每一个被合并的信号必须调用过一次sendNext,才能触发合并的信号,最后以元组的形式发出。
reduce
当信号发出的内容是元组时,可以使用它将元组聚合成一个值。
将对姓名和密码的验证结果的两种信合合并成一个信号,再通过reduce返回最终是否验证通过的信号。
1 2 3 4 5 6 7
| RACSignal *signUpActiveSignal = [RACSignal combineLatest:@[validUsernameSignal, validPasswordSignal] reduce:^id(NSNumber *usernameValid, NSNumber *passwordValid ) { return @([usernameValid boolValue] && [passwordValid boolValue]); }];
|
数据流的状态:
doNext
在每次信号执行订阅者的Next方法之前,会调用这个方法,它对信号本身不会产生影响。
在按钮点击之后,会有一系列验证的过程,在这期间,按钮不能被再次点击,等待验证结果出来之后,在恢复按钮未正常使用的状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| [[[[self.signInButton rac_signalForControlEvents:UIControlEventTouchUpInside] doNext:^(id x) { self.signInButton.enabled = NO; self.signInFailureText.hidden = YES; }] flattenMap:^id(id x) { return [self signInSignal]; }] subscribeNext:^(NSNumber *signedIn) { self.signInButton.enabled = YES; BOOL success = [signedIn boolValue]; self.signInFailureText.hidden = success; if (success) { [self performSegueWithIdentifier:@"signInSuccess" sender:self]; } }];
|
数据流的状态:
then
用于连接两个信号,当第一个信号完成之后(前一个信号调用sendCompleted),才会连接then返回的信号,then之前的信号会被忽略
在得到否有权限获得账户信息之后,通过then获取搜索文本的信号,再验证搜索文本的有效性,最后获取有效的搜索文本。如果获取账号信息这一步出现错误,直接调用最后的error的block。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| [[[[self requestAccessToTwitterSignal] then:^RACSignal *{ @strongify(self) return self.searchText.rac_textSignal; }] filter:^BOOL(NSString *text) { @strongify(self) return [self isValidSearchText:text]; }] subscribeNext:^(id x) { NSLog(@"%@", x); } error:^(NSError *error) { NSLog(@"An error occurred: %@", error); }];
|
数据流如下图:
deliverOn
信号传递切换到指定线程中
现在创建一个信号在后台线程下载一个图片,可以考虑使用SDWebImage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| -(RACSignal *)signalForLoadingImage:(NSString *)imageUrl { RACScheduler *scheduler = [RACScheduler schedulerWithPriority:RACSchedulerPriorityBackground]; return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber ) { NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl] ]; UIImage *image = [UIImage imageWithData:data]; [subscriber sendNext:image]; [subscriber sendCompleted]; return nil; }] subscribeOn:scheduler]; }
|
图片下载完成之后切换到主线程,设置图片的显示
1 2 3 4 5 6 7
| cell.twitterAvatarView.image = nil; [[[self signalForLoadingImage:tweet.profileImageUrl] deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(UIImage *image) { cell.twitterAvatarView.image = image; }];
|
throttle
节流,可以设置某一段时间内不发送信号,等过了这段时间,将最新的信号发出
在用户输入搜索文字的时候,每一次textChange都会触发搜索结果的网络请求。请求次数过于频繁且没有必要,因为有时候可能是用户还没有输完而已。并且结果频繁的显示清空体验也不好。现在通过throttle处理每经过0.5秒处理一次搜索请求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| [[[[[[[self requestAccessToTwitterSignal] then:^RACSignal *{ @strongify(self) return self.searchText.rac_textSignal; }] filter:^BOOL(NSString *text) { @strongify(self) return [self isValidSearchText:text]; }] throttle:0.5] flattenMap:^RACStream *(NSString *text) { @strongify(self) return [self signalForSearchWithText:text]; }] deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDictionary *jsonSearchResult) { NSArray *statuses = jsonSearchResult[@"statuses"]; NSArray *tweets = [statuses linq_select:^id(id tweet) { return [RWTweet tweetWithStatus:tweet]; }]; [self.resultsViewController displayTweets:tweets]; } error:^(NSError *error) { NSLog(@"An error occurred: %@", error); }];
|
经过最后的处理,数据流如下图所示:
在requestAccessToTwitterSignal和signalForSearchWithText都有可能产生error,最后都会直接调用subscribleNext:error方法。
takeUnitl
(RACSignal *) 获取信号直到某个信号执行完成
比如,监听某个文本框的文本改变直到当前对象被销毁
1
| [_textField.rac_textSignal takeUntil:self.rac_willDeallocSignal];
|
skip
(NSUInteger) 跳过几个信号,选择忽略不处理
第一次输入不被监听
1 2 3 4
| [[_textField.rac_textSignal skip:1] subscribeNext:^(id x) { NSLog(@"%@",x); }];
|
常见的宏
RAC(object, property)
1 2
| RAC(_titleLabel, text) = [viewModel.titleSignal takeUntil:self. rac_prepareForReuseSignal];
|
表示将一个对象的属性和一个signal进行绑定,signal每产生一个value,都会自动执行如下代码
1
| [TARGET setValue:value ?: NIL_VALUE forKeyPath:KEYPATH];
|
RACObserve(TARGET, KEYPATH)
返回一个signal,检测target的keypath属性
RAC和RACObserve用在一起实现双向绑定
1
| RAC(self.outputLabel, text) = RACObserve(self.model, name);
|