ReactiveCocoa是FRP(Functional Reactive Program)思想在iOS的Cocoa框架中的具体实现,可以说是一大神兵利器,尤其是在异步处理、事件处理、切面处理方面非常的好用。对ReactiveCocoa不是很了解的同学可以参考美团的文章和雷纯峰的博客。
接下来我将演示RAC对异步处理的优雅使用
假如现在有一个搜索业务,类似于百度主页的搜索.当用户输入一个文字而且0.3秒之内没有新的输入,我们就默认用户有搜索需求.程序就需要从本地、网络、内存等地方获取结果,并返回后渲染UI。不论是哪一个请求先返回都要显示在屏幕上,后返回的都需要依次添加到后面.当用户改变了输入框中的文字,之前的请求结果没有返回的都不再需要了,当前次的请求会去获取最新的数据并显示.
现在我们来简单实现一下,ReactiveCocoa使用的是2.5
版本,该版本是RAC使用Objective-C的最终版实现.
/// 获取搜索结果信号
- (RACSignal *)searchWithKey:(NSString *)key
{
return [[self searchFromDatabaseWithKey:key] merge:
[self searchFromNetworkWithKey:key]];
}
/// 从数据库中获取
- (RACSignal *)searchFromDatabaseWithKey:(NSString *)key
{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[RACScheduler schedulerWithPriority:RACSchedulerPriorityBackground] afterDelay:0.3 schedule:^{
[subscriber sendNext:@[@"Database data-1",@"Database data-2"]];
[subscriber sendCompleted];
}];
return nil;
}];
}
/// 从网络中获取
- (RACSignal *)searchFromNetworkWithKey:(NSString *)key
{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[RACScheduler schedulerWithPriority:RACSchedulerPriorityBackground] afterDelay:3 schedule:^{
[subscriber sendNext:@[@"Network-1",@"Network-2"]];
[subscriber sendCompleted];
}];
return nil;
}];
}
首先我们需要两个信号,分别是从数据库、网络获取的信号,当然实际情况可能从N个地方获取,这里模拟了一下延时依次是0.3秒、3秒,对这个两个信号merge
,生成一个新的汇总信号.这里不使用用concat
或者zipWith
是因为它们会对信号顺序有执行依赖关系,不符合需求.
- (void)viewDidLoad
{
[super viewDidLoad];
// 获取搜索信号
RACSignal *searchTextFieldSignal = [self.searchTextField rac_textSignal];
// 根据关键字搜索,返回聚合的高阶信号
RACSignal *(^searchSignalBlcok)(NSString *) = ^RACSignal *(NSString *keyword){
return
[[self searchWithKey:keyword] scanWithStart:[NSMutableArray array]
reduce:^id(NSMutableArray *running, NSArray *next) {
[running addObjectsFromArray:next];
return running;
}];
};
// 对关键字位数过滤
BOOL (^keywordFilter)(NSString *) = ^BOOL (NSString *keyword){
return keyword.length > 0;
};
// 加工信号并订阅
[[[[[[searchTextFieldSignal throttle:0.3]
filter:keywordFilter]
map:searchSignalBlcok]
switchToLatest]
deliverOnMainThread]
subscribeNext:^(NSMutableArray *searchArray) {
// 渲染UI
[self.datas removeAllObjects];
[self.datas addObjectsFromArray:searchArray];
[self.tableView reloadData];
}];
1.searchSignalBlcok
会先获取搜索结果信号,然后利用scanWithStart:reduce:
进行聚合操作,并将聚合后的数据放入集合里面,然后生成一个高阶信号,稍后会降阶.
2.获取文本框的输入信号,通过throttle:
获取用户需要搜索的行为,再filter:
过滤掉空文本,再map:
生成一个高阶信号,此高阶信号的信号中保存着刚才聚合后的数组,利用switchToLatest
降阶,会取消掉之前还没有返回结果的信号,即searchSignalBlcok
中产生的信号,这一步降阶非常关键.如果不用switchToLatest
降阶,就会产生上一次的结果由于延时会被显示出来,造成问题.然再deliver
到主线程,最后一步就是订阅最终的信号,获取searchArray
即可.
可以看到,使用RAC来实现这样的业务逻辑非常的简单,而且流程非常清晰.