您的位置:首页 > Web前端 > React

ReactiveCocoa的学习笔记

2016-02-28 22:26 387 查看
最近看了一些关于ReactiveCocoa的东西,现将其纪录下来。

ReactiveCocoa是由Github工程师们开发的一个应用于iOS和OS X开发的函数响应式编程新框架。

在iOS开发中,按钮的点击,收到网络消息,属性的变化(通过KVO)等都是不同的事件,这些事件都用不同的方式来处理,如代理方法、block 回调、target-action 机制、通知、KVO 等,而ReactiveCocoa为事件定义了一个标准接口,可以通过一个统一通用的消息传递分发机制来处理各种不同的事件。

信号源(RACSignal)

在 ReactiveCocoa 中,信号源代表的是随着时间而改变的值流。RACSignal 可以向订阅者发送三种不同类型的事件:

next :RACSignal 通过 next 事件向订阅者传送新的值,并且这个值可以为 nil ;

error :RACSignal 通过 error 事件向订阅者表明信号在正常结束前发生了错误;

completed :RACSignal 通过 completed 事件向订阅者表明信号已经正常结束,不会再有后续的值传送给订阅者。

ReactiveCocoa 中的值流只包含正常的值,即通过 next 事件传送的值,并不包括 error 和 completed 事件,它们需要被特殊处理。通常情况下,一个信号的生命周期是由任意个 next 事件和一个 error 事件或一个 completed 事件组成的。

对于 RACSignal 类簇来说,最核心的方法莫过于 -subscribe: 了,这个方法封装了订阅者对信号源的一次订阅过程,它是订阅者与信号源产生联系的唯一入口。因此,对于 RACSignal 的所有子类来说,这个方法的实现逻辑就代表了该子类的具体订阅行为,是区分不同子类的关键所在。同时,这也是为什么 RACSignal 中的 -subscribe: 方法是一个抽象方法,并且必须要让子类实现的原因:

- (RACDisposable *)subscribe:(id)subscriber {
NSCAssert(NO, @"This method must be overridden by subclasses");
return nil;
}


订阅者(RACSubscriber)

Subscriber 负责承接signal 传递的数据。为了获取信号源中的值,需要对信号源进行订阅。在 ReactiveCocoa 中,订阅者是一个抽象的概念,所有实现了 RACSubscriber 协议的类都可以作为信号源的订阅者。一个signal没有Subscriber时什么也不干,此时处于冷状态。当有了Subscriber时才可以传递消息,处于热状态。

如下代码,只是创建了一个信号,并没有订阅者,所以处在冷信号阶段。

RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
NSLog(@"triggered");
[subscriber sendNext:@"signal"];
[subscriber sendCompleted];
return nil;
}];


订阅信号,使信号处于热状态,如下代码:

[[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
NSLog(@"triggered");
[subscriber sendNext:@"signal"];
[subscriber sendCompleted];
return nil;
}] subscribeCompleted:^{
NSLog(@"subscription");
}];


ReactiveCocoa框架中常见Category

RAC在UIkit中加的Category,如:

UITextField+RACSignalSupport.h
UIButton+RACCommandSupport.h
UIAlertView+RACSignalSupport.h
...


常用的UIKit框架中的类也都有添加相应的Category。比如UIAlertView,利用RAC提供的分类就不需要再用Delegate了。

UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"" message:@"Alert" delegate:nil cancelButtonTitle:@"YES" otherButtonTitles:@"NO", nil];
[[alertView rac_buttonClickedSignal] subscribeNext:^(NSNumber *indexNumber) {
if ([indexNumber intValue] == 1) {
NSLog(@"you selected NO");
} else {
NSLog(@"you selected YES");
}
}];
[alertView show];


其他常见类的Category

NSArray+RACSequenceAdditions.h
NSDictionary+RACSequenceAdditions.h
NSNotificationCenter+RACSupport.h
...


数据转换

Signal是很灵活的,它可以被修改(map), 过滤(filter), 叠加(combine), 串联(chain),这有助于应对更加复杂环境。

使用map操作通过block改变了事件中的数据。使用filter对事件中的数据进行过滤。

[[[self.testTextField.rac_textSignal
map:^id(NSString* text){
return @(text.length);
}]
filter:^BOOL(NSNumber*length){
return[length integerValue] > 5;
}]
subscribeNext:^(id x){
NSLog(@"%@", x);
}];


map以NSString为输入,取字符串的长度,返回一个NSNumber。对传入的NSNumber类型数据,使用filter进行过滤,在输入数据后,当字符个数大于5时,打印出来的是所有大于5的数字。

对信号进行聚合(combine)

RAC(self.logInButton, enabled) = [RACSignal
combineLatest:@[
self.usernameTextField.rac_textSignal,
self.passwordTextField.rac_textSignal
] reduce:^(NSString *username, NSString *password, NSNumber *loggingIn, NSNumber *loggedIn) {
return @(username.length > 0 && password.length > 0 );
}];


RAC(self.logInButton, enabled)运用到了RAC中定义的宏,RAC宏允许直接把信号的输出应用到对象的属性上。RAC宏有两个参数,第一个是需要设置属性值的对象,第二个是属性名。每次信号产生一个next事件,传递过来的值都会应用到该属性上。使用combineLatest:reduce:方法把上面的两个信号聚合在一起,并生成一个新的信号。每次这两个源信号的任何一个产生新值时,reduce block都会执行,block的返回值会发给下一个信号。RACsignal的这个方法可以聚合任意数量的信号,reduce 中block的参数和每个源信号相关。

参考的资料:

/article/1225656.html

http://www.cocoachina.com/cms/wap.php?action=article&id=14880

http://www.cocoachina.com/industry/20140115/7702.html

http://benbeng.leanote.com/post/ReactiveCocoaTutorial-part2
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: