mirror of
https://github.com/zhigang1992/ReactiveCocoa.git
synced 2026-04-29 21:05:36 +08:00
Merge branch 'master' into 2.0-development
This commit is contained in:
@@ -6,6 +6,11 @@ and includes examples demonstrating their use.
|
||||
Operators that apply to [sequences][Sequences] _and_ [signals][Signals] are
|
||||
known as [stream][Streams] operators.
|
||||
|
||||
**[Performing side effects with signals](#performing-side-effects-with-signals)**
|
||||
|
||||
1. [Subscription](#subscription)
|
||||
1. [Injecting effects](#injecting-effects)
|
||||
|
||||
**[Transforming streams](#transforming-streams)**
|
||||
|
||||
1. [Mapping](#mapping)
|
||||
@@ -24,6 +29,85 @@ known as [stream][Streams] operators.
|
||||
1. [Combining latest values](#combining-latest-values)
|
||||
1. [Switching](#switching)
|
||||
|
||||
## Performing side effects with signals
|
||||
|
||||
Most signals start out "cold," which means that they will not do any work until
|
||||
[subscription](#subscription).
|
||||
|
||||
Upon subscription, a signal or its [subscribers][Subscription] can perform _side
|
||||
effects_, like logging to the console, making a network request, updating the
|
||||
user interface, etc.
|
||||
|
||||
Side effects can also be [injected](#injecting-effects) into a signal, where
|
||||
they won't be performed immediately, but will instead take effect with each
|
||||
subscription later.
|
||||
|
||||
### Subscription
|
||||
|
||||
The [-subscribe…][RACSignal] methods give you access to the current and future values in a signal:
|
||||
|
||||
```objc
|
||||
RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;
|
||||
|
||||
// Outputs: A B C D
|
||||
[letters subscribeNext:^(NSString *x) {
|
||||
NSLog(@"%@", x);
|
||||
}];
|
||||
```
|
||||
|
||||
For a cold signal, side effects will be performed once _per subscription_:
|
||||
|
||||
```objc
|
||||
__block unsigned subscriptions = 0;
|
||||
|
||||
RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
|
||||
subscriptions++;
|
||||
[subscriber sendCompleted];
|
||||
return nil;
|
||||
}];
|
||||
|
||||
// Outputs:
|
||||
// subscription 1
|
||||
[loggingSignal subscribeCompleted:^{
|
||||
NSLog(@"subscription %u", subscriptions);
|
||||
}];
|
||||
|
||||
// Outputs:
|
||||
// subscription 2
|
||||
[loggingSignal subscribeCompleted:^{
|
||||
NSLog(@"subscription %u", subscriptions);
|
||||
}];
|
||||
```
|
||||
|
||||
This behavior can be changed using a [connection][Connections].
|
||||
|
||||
### Injecting effects
|
||||
|
||||
The [-do…][RACSignal+Operations] methods add side effects to a signal without actually
|
||||
subscribing to it:
|
||||
|
||||
```objc
|
||||
__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);
|
||||
}];
|
||||
```
|
||||
|
||||
## Transforming streams
|
||||
|
||||
These operators transform a single stream into a new stream.
|
||||
@@ -269,6 +353,7 @@ RACSignal *switched = [signalOfSignals switchToLatest];
|
||||
[letters sendNext:@"D"];
|
||||
```
|
||||
|
||||
[Connections]: FrameworkOverview.md#connections
|
||||
[RACSequence]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSequence.h
|
||||
[RACSignal]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSignal.h
|
||||
[RACSignal+Operations]: ../ReactiveCocoaFramework/ReactiveCocoa/RACSignal+Operations.h
|
||||
@@ -276,3 +361,4 @@ RACSignal *switched = [signalOfSignals switchToLatest];
|
||||
[Sequences]: FrameworkOverview.md#sequences
|
||||
[Signals]: FrameworkOverview.md#signals
|
||||
[Streams]: FrameworkOverview.md#streams
|
||||
[Subscription]: FrameworkOverview.md#subscription
|
||||
|
||||
77
README.md
77
README.md
@@ -8,51 +8,46 @@ If you're already familiar with functional reactive programming or know the basi
|
||||
premise of ReactiveCocoa, check out the [Documentation][] folder for a framework
|
||||
overview and more in-depth information about how it all works in practice.
|
||||
|
||||
**Table of Contents**
|
||||
## New to ReactiveCocoa?
|
||||
|
||||
1. [Functional reactive programming](#functional-reactive-programming)
|
||||
1. [FRP with ReactiveCocoa](#frp-with-reactivecocoa)
|
||||
ReactiveCocoa is documented like crazy, and there's a wealth of introductory
|
||||
material available to explain what RAC is and how you can use it.
|
||||
|
||||
If you want to learn more, we recommend these resources, roughly in order:
|
||||
|
||||
1. [Introduction](#introduction)
|
||||
1. [When to use ReactiveCocoa](#when-to-use-reactivecocoa)
|
||||
1. [Importing ReactiveCocoa](#importing-reactivecocoa)
|
||||
1. [More Info](#more-info)
|
||||
1. [Framework Overview][]
|
||||
1. [Basic Operators][]
|
||||
1. [Header documentation](ReactiveCocoaFramework/ReactiveCocoa)
|
||||
1. Previously answered [Stack Overflow](https://github.com/ReactiveCocoa/ReactiveCocoa/wiki)
|
||||
questions and [GitHub issues](https://github.com/ReactiveCocoa/ReactiveCocoa/issues?labels=question&state=closed)
|
||||
1. The rest of the [Documentation][] folder
|
||||
|
||||
## Functional reactive programming
|
||||
## Introduction
|
||||
|
||||
Functional Reactive Programming (FRP) is a programming paradigm for writing
|
||||
software that reacts to change.
|
||||
ReactiveCocoa is an implementation of [functional reactive
|
||||
programming](http://blog.maybeapps.com/post/42894317939/input-and-output).
|
||||
Rather than using mutable variables which are replaced and modified in-place,
|
||||
RAC provides signals (represented by `RACSignal`) that capture present and
|
||||
future values.
|
||||
|
||||
FRP is built on the abstraction of values over time. Rather than capturing
|
||||
a value at a particular time, FRP provides signals that capture the past,
|
||||
present, and future value. These signals can be reasoned about, chained,
|
||||
composed, and reacted to.
|
||||
By chaining, combining, and reacting to signals, software can be written
|
||||
declaratively, without the need for code that continually observes and updates
|
||||
values.
|
||||
|
||||
By combining signals, software can be written declaratively, without the need
|
||||
for code that continually observes and updates values. A text field can be
|
||||
directly set to always show the current timestamp, for example, instead of using
|
||||
additional code that watches the clock and updates the text field every second.
|
||||
For example, a text field can be bound to the latest time, even as it changes,
|
||||
instead of using additional code that watches the clock and updates the
|
||||
text field every second. It works much like KVO, but with blocks instead of
|
||||
overriding `-observeValueForKeyPath:ofObject:change:context:`.
|
||||
|
||||
Signals can also represent asynchronous operations, much like [futures and
|
||||
promises][]. This greatly simplifies asynchronous software, including networking
|
||||
code.
|
||||
|
||||
One of the major advantages of FRP is that it provides a single, unified
|
||||
approach to dealing with different types of reactive, asynchronous behaviors.
|
||||
|
||||
Here are some resources for learning more about FRP:
|
||||
|
||||
* [What is FRP? - Elm Language](http://elm-lang.org/learn/What-is-FRP.elm)
|
||||
* [What is Functional Reactive Programming - Stack Overflow](http://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming/1030631#1030631)
|
||||
|
||||
[Josh Abernathy](https://github.com/joshaber) also has [a blog post about FRP](http://blog.maybeapps.com/post/42894317939/input-and-output).
|
||||
|
||||
## FRP with ReactiveCocoa
|
||||
|
||||
Signals in ReactiveCocoa (RAC) are represented using `RACSignal`. Signals are
|
||||
streams of values that can be observed and transformed.
|
||||
|
||||
Applications that are built with RAC use signals to propagate changes. It works
|
||||
much like KVO, but with blocks instead of overriding
|
||||
`-observeValueForKeyPath:ofObject:change:context:`.
|
||||
One of the major advantages of RAC is that it provides a single, unified
|
||||
approach to dealing with asynchronous behaviors, including delegate methods,
|
||||
callback blocks, target-action mechanisms, notifications, and KVO.
|
||||
|
||||
Here's a simple example:
|
||||
|
||||
@@ -83,10 +78,9 @@ But unlike KVO notifications, signals can be chained together and operated on:
|
||||
}];
|
||||
```
|
||||
|
||||
Signals can also be used to derive state, which is a key component of FRP.
|
||||
Instead of observing properties and setting other properties in response to the
|
||||
new values, RAC makes it possible to express properties in terms of signals and
|
||||
operations:
|
||||
Signals can also be used to derive state. Instead of observing properties and
|
||||
setting other properties in response to the new values, RAC makes it possible to
|
||||
express properties in terms of signals and operations:
|
||||
|
||||
```objc
|
||||
// Create a one-way binding so that self.createEnabled will be
|
||||
@@ -515,6 +509,13 @@ out there:
|
||||
* [101 Rx Samples](http://rxwiki.wikidot.com/101samples)
|
||||
* [Programming Reactive Extensions and LINQ](http://www.amazon.com/Programming-Reactive-Extensions-Jesse-Liberty/dp/1430237473)
|
||||
|
||||
RAC and Rx are both implementations of functional reactive programming. Here are
|
||||
some more resources for learning about FRP:
|
||||
|
||||
* [What is FRP? - Elm Language](http://elm-lang.org/learn/What-is-FRP.elm)
|
||||
* [What is Functional Reactive Programming - Stack Overflow](http://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming/1030631#1030631)
|
||||
|
||||
[Basic Operators]: Documentation/BasicOperators.md
|
||||
[Documentation]: Documentation
|
||||
[Framework Overview]: Documentation/FrameworkOverview.md
|
||||
[Functional Reactive Programming]: http://en.wikipedia.org/wiki/Functional_reactive_programming
|
||||
|
||||
@@ -221,8 +221,10 @@ describe(@"subscribing", ^{
|
||||
});
|
||||
expect(currentScheduler).willNot.beNil();
|
||||
});
|
||||
});
|
||||
|
||||
it(@"should support -takeUntil:", ^{
|
||||
describe(@"-takeUntil:", ^{
|
||||
it(@"should support value as trigger", ^{
|
||||
__block BOOL shouldBeGettingItems = YES;
|
||||
RACSubject *subject = [RACSubject subject];
|
||||
RACSubject *cutOffSubject = [RACSubject subject];
|
||||
@@ -240,7 +242,7 @@ describe(@"subscribing", ^{
|
||||
[subject sendNext:@"test 3"];
|
||||
});
|
||||
|
||||
it(@"should support -takeUntil: with completion as trigger", ^{
|
||||
it(@"should support completion as trigger", ^{
|
||||
__block BOOL shouldBeGettingItems = YES;
|
||||
RACSubject *subject = [RACSubject subject];
|
||||
RACSubject *cutOffSubject = [RACSubject subject];
|
||||
@@ -253,6 +255,23 @@ describe(@"subscribing", ^{
|
||||
shouldBeGettingItems = NO;
|
||||
[subject sendNext:@"should not go through"];
|
||||
});
|
||||
|
||||
it(@"should squelch any values sent immediately upon subscription", ^{
|
||||
RACSignal *valueSignal = [RACSignal return:RACUnit.defaultUnit];
|
||||
RACSignal *cutOffSignal = [RACSignal empty];
|
||||
|
||||
__block BOOL gotNext = NO;
|
||||
__block BOOL completed = NO;
|
||||
|
||||
[[valueSignal takeUntil:cutOffSignal] subscribeNext:^(id _) {
|
||||
gotNext = YES;
|
||||
} completed:^{
|
||||
completed = YES;
|
||||
}];
|
||||
|
||||
expect(gotNext).to.beFalsy();
|
||||
expect(completed).to.beTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe(@"disposal", ^{
|
||||
|
||||
Reference in New Issue
Block a user