转载

初识 Reactive Cocoa

ReactiveCocoa 是一个用于将状态的变化转化成事件流的框架。

它可以解决三个问题:

  • 开发过程中关于界面的状态信息太多,不易维护。
  • 统一 各种消息传递的方式 。
  • 绑定功能可以配合 MVVM 使用。

基本概念

简而言之,Singal 以 stream 的方式将 event 发送给 Observer。

Event

Event 代表值发生变化,或者有用户操作,分为 4 种:

  • Next:signal 产生了新的值。
  • Failed:signal 执行发生了错误,不再产生新的值。
  • Completed:signal 执行结束,不再产生新的值。
  • Interrupted:signal 被中止,不再产生新的值。

Singal

Signal 类似于一个 event 源头,event 通过 signal 传递。用户只能通过 subscribe 来开始 有序地、被动地 获取 signal 里面的值。一一个 signal 的生命周期通常是一个或多个 Next event,然后以 Failed、Completed 和 Interrupted 中的一种 event 结束,不受 subscriber 的影响。

用户可以使用基本操作符对 Signal 进行一些操作,如过滤(filter)、映射(map)等。

Observer

通过 subscribe 一个 signal 来接受 event 的对象。

RACCommand

通常用来执行一个 UI 事件发生后要处理的任务。

RACSubject

一个 signal 的子类,是一个可以手动发送 Next、Failed 等事件的 signal。通常用来将 non-RAC 的代码 bridge 成 RAC 的代码。

基本操作

添加副作用

  • -subscribe...:

用来订阅一个 signal 的 Next、Failed、Completed 和 Interrupted 事件中的一种或几种:

RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;

// Outputs: A B C D E F G H I
[letters subscribeNext:^(NSString *x) {
NSLog(@"%@", x);
}];
  • -do...:

用来添加副作用,当 Next、Failed、Completed 和 Interrupted 事件中的一种发生时执行 do... 中的 block。

__block unsigned subscriptions = 0;

RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
subscriptions++;
[subscriber sendCompleted];
return nil;
}];

// Does not output anything yet
loggingSignal = [loggingSignal doCompleted:^{
NSLog(@"about to complete subscription %u", subscriptions);
}];

// Outputs:
// about to complete subscription 1
// subscription 1
[loggingSignal subscribeCompleted:^{
NSLog(@"subscription %u", subscriptions);
}];

转换 stream

  • -map:

将一个 stream 中的值转换成其他的值,组成一个新的 stream。

RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;

// Contains: AA BB CC DD EE FF GG HH II
RACSequence *mapped = [letters map:^(NSString *value) {
return [value stringByAppendingString:value];
}];
  • -filter:

用来过滤 stream 中的值,传入的 block 返回一个 BOOL,来决定是否将值放入新的 stream 中。

RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;

// Contains: 2 4 6 8
RACSequence *filtered = [numbers filter:^ BOOL (NSString *value) {
return (value.intValue % 2) == 0;
}];

组合 stream

  • -concat:

将一个 stream 的值追加到另一个 stream 的值后面。

RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;

// Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9
RACSequence *concatenated = [letters concat:numbers];
  • -flatten:

如果一个 signal 的 stream 中的值也是一个 stream, -flatten: 会将子 stream 中的值取出,组成一个新的 stream。组合的效果可能是 concat:

RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *sequenceOfSequences = @[ letters, numbers ].rac_sequence;

// Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9
RACSequence *flattened = [sequenceOfSequences flatten];

或者 merge:

RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSignal *signalOfSignals = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
[subscriber sendNext:letters];
[subscriber sendNext:numbers];
[subscriber sendCompleted];
return nil;
}];

RACSignal *flattened = [signalOfSignals flatten];

// Outputs: A 1 B C 2
[flattened subscribeNext:^(NSString *x) {
NSLog(@"%@", x);
}];

[letters sendNext:@"A"];
[numbers sendNext:@"1"];
[letters sendNext:@"B"];
[letters sendNext:@"C"];
[numbers sendNext:@"2"];
  • -flattenMap:

当一个 stream 的值也是 stream 时,将子 stream 中的值转换后,组成一个新的 stream。

-flattenMap: = -map: + -flatten:

RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;

// Contains: 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9
RACSequence *extended = [numbers flattenMap:^(NSString *num) {
return @[ num, num ].rac_sequence;
}];

// Contains: 1_ 3_ 5_ 7_ 9_
RACSequence *edited = [numbers flattenMap:^(NSString *num) {
if (num.intValue % 2 == 0) {
return [RACSequence empty];
} else {
NSString *newNum = [num stringByAppendingString:@"_"];
return [RACSequence return:newNum];
}
}];

组合 signal

  • -then:

当一个 signal 执行完成之后,subscriber 后续只会收到新返回的 signal 的值,如同订阅了新的 signal 一样。

RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;

// The new signal only contains: 1 2 3 4 5 6 7 8 9
//
// But when subscribed to, it also outputs: A B C D E F G H I
RACSignal *sequenced = [[letters
doNext:^(NSString *letter) {
NSLog(@"%@", letter);
}]
then:^{
return [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence.signal;
}];
  • +merge:

将多个 signal 合并为一个,一旦任一合并前的 signal 中产生新的值,新的 signal 立即产生同样的值

RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSignal *merged = [RACSignal merge:@[ letters, numbers ]];

// Outputs: A 1 B C 2
[merged subscribeNext:^(NSString *x) {
NSLog(@"%@", x);
}];

[letters sendNext:@"A"];
[numbers sendNext:@"1"];
[letters sendNext:@"B"];
[letters sendNext:@"C"];
[numbers sendNext:@"2"];
  • +combineLatest:+combineLatest:reduce:

将多个 signal 合并成一个。与 +merge: 不同的是,这两个方法产生的新的 signal 只有在所有的合并前 signal 至少产生一个新的值 时,才将 所有的值 (以元组的方式)返回。

RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSignal *combined = [RACSignal
combineLatest:@[ letters, numbers ]
reduce:^(NSString *letter, NSString *number) {
return [letter stringByAppendingString:number];
}];

// Outputs: B1 B2 C2 C3
[combined subscribeNext:^(id x) {
NSLog(@"%@", x);
}];

[letters sendNext:@"A"];
[letters sendNext:@"B"];
[numbers sendNext:@"1"];
[numbers sendNext:@"2"];
[letters sendNext:@"C"];
[numbers sendNext:@"3"];
  • -switchToLatest

用来处理由 signal 组成的 signal (signal-of-signals),总是返回最新的外层 signal 中的最新的 signal 的值。

RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSubject *signalOfSignals = [RACSubject subject];

RACSignal *switched = [signalOfSignals switchToLatest];

// Outputs: A B 1 D
[switched subscribeNext:^(NSString *x) {
NSLog(@"%@", x);
}];

[signalOfSignals sendNext:letters];
[letters sendNext:@"A"];
[letters sendNext:@"B"];

[signalOfSignals sendNext:numbers];
[letters sendNext:@"C"];
[numbers sendNext:@"1"];

[signalOfSignals sendNext:letters];
[numbers sendNext:@"2"];
[letters sendNext:@"D"];

其他

  • take:(NSUInteger)n

仅仅值的 前 n 次 的变化会继续传递下去。

  • distinctUntilChanged

仅当 signal 的值与上一次不同时才会继续传递下去。

  • throttle:(NSTimeInterval)interval

在 interval 的时间内发生的值的变化仅传递最后一个。

统一消息传递机制

代替 KVO

RACSignal *isReachable = [RACObserve(reachabilityManager, networkReachabilityStatus)
map:^(NSNumber *networkReachabilityStatus) {
switch (networkReachabilityStatus.intValue) {
case AFNetworkReachabilityStatusReachableViaWWAN:
case AFNetworkReachabilityStatusReachableViaWiFi:
return @YES;
}
return @NO;
}];

代替 Notification

[[[[NSNotificationCenter defaultCenter] rac_addObserverForName:kReachabilityChangedNotification object:nil]
subscribeNext:^(id x) {
NSLog(@"notification: %@", x);
}]
;

并且不需要 removeObserver。

代替 Delegate

RACSignal *didStartLoadSignal = [self.webView rac_signalForSelector:@selector(webViewDidStartLoad:) fromProtocol:@protocol(UIWebViewDelegate)];

UIView Categories

ReactiveCocoa 提供了许多 UIView 的 Category 用来快速将 UI 的事件、属性转换成 signal。例如 rac_textSignal 是 UITextField 的 text 属性转换成的信号; rac_buttonClickedSignal 是 UIAlertView 的按钮点击的信号。

我们订阅这些信号就可以摆脱各种 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(@"NO");
} else {
NSLog(@"YES");
}
}];
[alertView show];

ReactiveCocoa 提供了一些非常方便的宏。

  • RACObserve(self, status) :将 status 转换成一个 signal,当其值发生变化时触发事件。作用与 KVO 相同。
  • RAC(self.textField, text) :将某个对象的某个属性绑定到一个 signal 的值上。
  • @weakify(self)@strongify(self) 用于避免循环引用。

Ref

原文  http://nixwang.com/2016/03/24/introduce-reactive-cococa/
正文到此结束
Loading...