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

ReactiveCocoa Tutorial – The Definitive Introduction

2016-03-14 19:32 816 查看
Part One

*ReactiveCocoa combines a couple of programming styles:

Functional Programming which makes use of higher order functions, i.e. functions which take other functions as their arguments.

Reactive Programming which focuses of data-flows and change propagation

For this reason, you might hear ReactiveCocoa described as a Functional Reactive Programming (or FRP) framework.

*ReactiveCocoa signals (represented by RACSignal) send a stream of events to their subscribers. There are three types of events to know: next, error and completed. A signal may send any number of next events before it terminates after an error, or it completes.

*RACSignal has a number of methods you can use to subscribe to these different event types. Each method takes one or more blocks, with the logic in your block executing when an event occurs. In this case, you can see that the subscribeNext: method was used
to supply a block that excites on each next event. 

*The ReactiveCocoa framework uses categories to add signals to many of the standard UIKit controls so you can add subscriptions to their events, which is where the rac_textSignal property on the text field came from.

*

    RACSignal *usernameSourceSignal =
self.usernameTextField.rac_textSignal;

    RACSignal *filteredUsername = [usernameSourceSignal filter:^BOOL(id value) {

        NSString *text = value;

        return text.length >3;

    }];

    [filteredUsername subscribeNext:^(id x) {

        NSLog(@"%@", x);

    }];
What you've created here is a very simple pipeline. It is the very essence of Reactive Programming, where you express your application's functionality in terms of data flows.

In the above diagram you can see that the rac_textSingal is the initial source of events. The data flows through a filter that only allows events to pass if they contain a string with a length that is greater than 3. The final step in the pipeline is subscribeNext:
where your block logs the event value.

At this point it's worth noting that the output of the filter operation is also an RACSignal. 

*The RAC macro allows you to assign the output of a signal to the property of an object. It takes two arguments, the first is the object that contains the property to set and the second is the property name. Each time the signal emits a next event, the value
that passes is assigned to the given property. 

*

 RACSignal *signUpActiveSignal =

    [RACSignal combineLatest:@[validUsernameSignal, validPasswordSignal]

                      reduce:^id(NSNumber *usernameValid,NSNumber *passwordValid) {

                          return
@([usernameValid boolValue] && [passwordValidboolValue]);

                      }];
The above code uses the combineLatest:reduce: method to combine the latest values emitted by validUsernameSignal and validPasswordSignal into a shiny new signal. Each time either of the two source signals emits a new value, the reduce block executes, and
the value it returns is sent as the next value of the combined signal.

*The result of these changes is the application no longer has private properties that indicate the current valid state of the two text fields. This is one of the key differences you'll find when you adopt a reactive style -- you don't need to use instance
variables to track transient state.

*

-(RACSignal *)signInSignal {

    return [RACSignal createSignal:^RACDisposable
*(id<RACSubscriber> subscriber) {

        [self.signInServicesignInWithUsername:self.usernameTextField.text

                                      password:self.passwordTextField.text

                                      complete:^(BOOL success) {

                                          [subscriber
sendNext:@(success)];

                                          [subscriber
sendCompleted];

                                      }];

        return nil;

    }];

}
The above code uses the createSignal: method on RACSignal for singal creation. The block that describes this signal is a single argument, and is passed to this method. When this signal has a subscriber, the code within this block executes.

The block is passed a single subscriber instance that adopt the RACSubscriber protocol, which has methods you invoke in order to emit events; you may also send any number of next events, terminated with either an error or complete event. In this case, it
sends a single next event to indicate whether the sign-in was a success, followed by a complete event.

The return type for this block is an RACDisposable object, and it allows you to perform any clean-up work that might be required when a subscription is cancelled or trashed. This signal does not have any clean-up requirements, hence nil is returned. 

*Side-effect: logic you want to execute within a pipeline when a next event occurs, but it does not actually change the nature of the event itself.

Part Two

*ReactiveCocoa maintains and retains its own global set of signals. If it has one or more subscribers, then the signal is active. If all subscribers are removed, the signal can be de-allocated.

After a completed or error event, a subscription removes itself automatically. Manual removal may be accomplished via RACDisposable.

The subscription methods on RACSignal all return an instance of RACDisposable that allows you to manually remove the subscription via the dispose method.

If you create a pipeline but do not subscribe to it, the pipeline never executes, this includes any side-effects such as doNext: blocks.

*Note: If you're interested in finding out what @weakify and @strongify actually do, within Xcode select Product -> Perform Action -> Preprocess "RWSearchForViewController". This will preprocess the view controller, expand all the macros and allow you to
see the final output. 

Take care when using instance variables within blocks. These will also result in the block capturing a strong reference to self. You can turn on a compiler warning to alert you if your code results in this problem. Search for retain within the project's
build settings to find the options.

*

    [[[[[[[self requestAccessToTwitterSignal]

          then:^RACSignal *{

              return
self.searchText.rac_textSignal;

          }]

         filter:^BOOL(NSString *text) {

             return [self isValidSearchText:text];

         }]

        throttle:0.5]

       flattenMap:^RACStream *(NSString *text) {

           return [self signalForSearchWithText:text];

       }]

      deliverOn:[RACSchedulermainThreadScheduler]]

     subscribeNext:^(NSDictionary *jsonSearchResult) {

         NSArray *statuses = jsonSearchResult[@"statuses"];

         NSArray *tweets = [statuses
linq_select:^id(id tweet) {

             return [RWTweet tweetWithStatus:tweet];

         }];

         [self.resultsViewControllerdisplayTweets:tweets];

     } error:^(NSError *error) {

         NSLog(@"An error occurred: %@", error);

     }];
The then method waits until a completed event is emitted, then subscribes to the signal returned by its block parameter. This effectively passes control from one signal to the next.

The then method passes error events through. Therefore the final subscribeNext:error block still receives errors emitted by the initial access-requesting step.

The application pipeline starts with the requestAccessToTwitterSignal then switches to the rac_textSignal. Meanwhile, next events pass through a filter and finally onto the subscription block. You can also see any error events emitted by the first step are
consumed by the same subscrieNext:error: block.

 As soon as a signal emits an error, it falls straight-through to the error-handling block. It is an exceptional flow.

*The operations shown above execute on the thread where the signal originally emitted its events.

*The deliverOn: pipeline step marshals the next event onto the main thread so that the subscribeNext: block can be safely executed.

*The throttle operation will only send a next event if another next event isn't received within the given time period.

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