ReactiveObjC 响应函数式框架
ReactiveObjC 是什么?
RAC,简单来说,就是信号,提供RACSignal
捕捉当前值和将来值的信号,通过对信号进行链接,组合和反应,软件可以以声明方式编写,而不需要连续观察和更新值的代码
// When self.username changes, logs the new name to the console. // // RACObserve(self, username) creates a new RACSignal that sends the current // value of self.username, then the new value whenever it changes. // -subscribeNext: will execute the block whenever the signal sends a value. [RACObserve(self, username) subscribeNext:^(NSString *newName) { NSLog(@"%@", newName); }];
ReactiveObjC 什么时候用?
响应式编程,处理异步或事件驱动的数据源,链接依赖的操作,并行独立的工作,简化收集转换
@weakify(self); @strongify(self);
ReactiveObjC的简单使用
1.监听按钮点击事件
@weakify(self)
[[self.submitBnt rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
@strongify(self)
//按钮点击后响应内容
}];
2.监听通知事件
@weakify(self)
[[[[NSNotificationCenter defaultCenter] rac_addObserverForName:KGestureLoginSueccessNotification object:nil] takeUntil:[self rac_willDeallocSignal]]subscribeNext:^(NSNotification * _Nullable x) {
@strongify(self)
//接受通知后响应内容
}];
3.监听某一对象的属性变化
宏定义RACObserve监听某个对象的某个属性的改变
[[[RACObserve(self.viewModel, page) distinctUntilChanged] deliverOnMainThread]
subscribeNext:^(id _Nullable x) {
@strongify(self)
//响应内容
}];
4.多个信号的处理
//当需要请求多个数据,在所有数据请求完成之后才进行更新UI或者其他操作。相当于多线程组
RACSignal *request1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber)
{
[subscriber sendNext:@1];
[subscriber sendCompleted];
return nil;
}];
RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber)
{
[subscriber sendNext:@2];
[subscriber sendCompleted];
return nil;
}];
RACSignal *request3 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber)
{
[subscriber sendNext:@3];
[subscriber sendCompleted];
return nil;
}];
//使用注意:几个信号,参数一的方法中就有几个参数,每个参数与此方法参数二中的数组一一对应。
[self rac_liftSelector:@selector(qb_updateDateWithRequest1:request2:request3:)
withSignalsFromArray:@[request1, request2, request3]];
//响应方法
- (void)qb_updateDateWithRequest1:(id)data1 request2:(id)data2 request3:(id)data3
{
//对应操作
}
5.接收代理信号
//注入实现的方法,只能返回“void”类型
[[self rac_signalForSelector:@selector(setButtonAction:)
fromProtocol:@protocol(CenterHeaderCellDelegate)]
subscribeNext:^(RACTuple * _Nullable x) {
//RACTuple可理解为数组,返回的为方法中的参数
}];
6.将信号分配给某一对象的属性,设置给定的信号。当信号完成时,分配的对象属性发生改变
RAC(self.footer.agreeBtn, enabled) = [RACSignal combineLatest:@[cell.codeTextField.rac_textSignal]
reduce:^id (NSString *codeString)
{
//监听文本框,当输入满足条件时,改变分配按钮的点击状态
return @(codeString.length >=6);
}];
//也可以多个条件聚合成一个,满足某一条件时,改变分配对象的属性
RAC(self.button, enabled) = [RACSignal combineLatest:@[self.nameTextField.rac_textSignal,
self.IdTextField.rac_textSignal]
reduce:^id(NSString *name,NSString *idString){
return @(idString.length > 0 && idString.length > 0);
}];
7.//map把原信号的值映射成一个新的值(还有其它concat(顺序),then(连接),merge(合并),zipWith(压缩),reduce(聚合)等信号操作方法,详情查询API)
[[self.textField.rac_textSignal map:^id _Nullable(NSString * _Nullable value) {
value = [NSString stringWithFormat:@"are you ok +%@", value];
return value;
}] subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
8.定时器
//定时器的2中用法
[[RACScheduler mainThreadScheduler] afterDelay:5.f schedule:^{ NSLog(@"5秒后执行"); }];
[[[RACSignal interval:2.f onScheduler:[RACScheduler mainThreadScheduler]] takeUntil:self.rac_willDeallocSignal ] subscribeNext:^(id x) { NSLog(@"每两秒执行一次"); }];
9.RAC even makes it easy to bind to the result of an asynchronous operation:
// Creates a one-way binding so that self.imageView.image will be set as the user's // avatar as soon as it's downloaded. // // The hypothetical -fetchUserWithUsername: method returns a signal which sends // the user. // // -deliverOn: creates new signals that will do their work on other queues. In // this example, it's used to move work to a background queue and then back to the main thread. // // -map: calls its block with each user that's fetched and returns a new // RACSignal that sends values returned from the block. RAC(self.imageView, image) = [[[[client fetchUserWithUsername:@"joshaber"] deliverOn:[RACScheduler scheduler]] map:^(User *user) { // Download the avatar (this is done on a background queue). return [[NSImage alloc] initWithContentsOfURL:user.avatarURL]; }] // Now the assignment will be done on the main thread. deliverOn:RACScheduler.mainThreadScheduler];
10.ReactiveObjC makes this pattern particularly easy:
[[[[client logIn]
then:^{
return [client loadCachedMessages];
}]
flattenMap:^(NSArray *messages) {
return [client fetchMessagesAfterMessage:messages.lastObject];
}]
subscribeError:^(NSError *error) {
[self presentError:error];
} completed:^{
NSLog(@"Fetched all messages.");
}];
11.Parallelizing independent work
__block NSArray *databaseObjects; __block NSArray *fileContents; NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init]; NSBlockOperation *databaseOperation = [NSBlockOperation blockOperationWithBlock:^{ databaseObjects = [databaseClient fetchObjectsMatchingPredicate:predicate]; }]; NSBlockOperation *filesOperation = [NSBlockOperation blockOperationWithBlock:^{ NSMutableArray *filesInProgress = [NSMutableArray array]; for (NSString *path in files) { [filesInProgress addObject:[NSData dataWithContentsOfFile:path]]; } fileContents = [filesInProgress copy]; }]; NSBlockOperation *finishOperation = [NSBlockOperation blockOperationWithBlock:^{ [self finishProcessingDatabaseObjects:databaseObjects fileContents:fileContents]; NSLog(@"Done processing"); }]; [finishOperation addDependency:databaseOperation]; [finishOperation addDependency:filesOperation]; [backgroundQueue addOperation:databaseOperation]; [backgroundQueue addOperation:filesOperation]; [backgroundQueue addOperation:finishOperation];
Github地址: https://github.com/ReactiveCocoa/ReactiveObjC