Reactive cocoa (referred to as RAC) is a functional responsive programming framework for iOS and OS development, which is open-source by Github. It provides a series of API s for combining and converting value streams
Before learning a framework, you should first understand the programming idea of the framework. Here, before introducing the responsive programming idea, you should first introduce the programming idea that you have touched before
1. Object oriented
Everything is object
It is a kind of programming language with object as basic program structure unit
Typical object-oriented programming languages include C + +, C ා, Java, etc
2. Process oriented
A process centered programming idea
C language is a process oriented language
3. Chain programming idea
It is to link multiple operations (multiple lines of code) together by dot (.) to make the code readable
Features of chain programming: the return value of a method is block, and block must have return value (its own object), block parameter (the value to be operated)
Typical framework: masonry framework.
4. Functional programming idea
All things flow
You don't need to think about the call order, you just need to know the result
Similar to the butterfly effect, an event will affect a lot of things. These events will spread like streams, and then affect the results
Representative: KVO application
5. Functional programming idea
Write the operation as a series of nested functions or method calls as much as possible
Features: each method must have a return value (its own object), a function or a block as a parameter, and a block parameter (the value to be operated) a block return value (the operation result)
Representative: ReactiveCocoa
6. ReactiveCocoa programming idea
Functional Programming
Reactive Programming
So reactive cocoa is described as a functional response programming (FRP) framework,
2, RACSiganl signal class
One of the core concepts of reactive cocoa is signal RACStream. There are two subclasses in RACStream - RACSignal and RACSequence; here we mainly talk about RACSignal;
RACSignal plays an important role in the whole database of reactive cocoa, and the transformation operation of RACSignal is one of the cores of the whole RACStream operation
Let's take a look at the whole process of RACSignal subscription
- (void)test { //Create signal RACSignal *single = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { //send message [subscriber sendNext:@"a"]; [subscriber sendNext:@"b"]; //Send completion [subscriber sendCompleted]; //wipe data return [RACDisposable disposableWithBlock:^{ //Executed when the subscriber is consumed //It will also be executed when the subscriber has finished sending or error NSLog(@"RACDisposable Of block"); }]; }]; //Subscription signal RACDisposable *disposable = [single subscribeNext:^(id _Nullable x) { NSLog(@"value = %@", x); } error:^(NSError * _Nullable error) { NSLog(@"error: %@", error); } completed:^{ NSLog(@"completed"); }]; //release [disposable dispose]; }
Before that, take a look at some subclasses of RACSignal
- RACDynamicSignal: dynamic signal. We use a block to implement subscription behavior. When we use RACSignal's + createSignal: method, we create an instance of this class
- RACEmptySignal: empty signal, used to implement the + empty method of RACSignal;
- RACReturnSignal: unary signal, used to implement the + return: method of RACSignal;
- RACErrorSignal: error signal, used to implement the + error: method of RACSignal;
- RACChannelTerminal: a channel terminal, representing a terminal of RACChannel, used to realize bidirectional binding
When RACSignal creates a signal, the bottom layer will call the createSignal method of RACDynamicSignal, as follows:
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe { return [RACDynamicSignal createSignal:didSubscribe]; }
The block here is a subscriber of ID < RACSubscriber > type. For this RACSubscriber, we can go to see some underlying implementations. The protocol method is as follows:
@protocol RACSubscriber <NSObject> @required /// Sends the next value to subscribers. - (void)sendNext:(nullable id)value; /// Sends the error to subscribers. - (void)sendError:(nullable NSError *)error; /// Sends completed to subscribers. - (void)sendCompleted; /// Sends the subscriber a disposable that represents one of its subscriptions. - (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;
RACSignal underlying implementation:
- To create a signal, first save the didSubscribe to the signal and it will not be triggered.
- When the signal is subscribed, that is to say, call the subscribeNext:nextBlock of signal
2.1 a subscriber will be created inside the subscribenext, and the nextBlock will be saved to the subscriber.
2.2 subscribeNext will call the didSubscribe of signl internally
2.3 when the signal subscription is completed and the data is not being sent, it is better to call the [subscriber sendCompleted] to finish sending;
When the subscription is completed, the [RACDisposable disposable] unsubscribe signal will be automatically called internally - [subscriber sendNext:@1] is called in didSubscribe of siganl.
3.1 the underlying layer of sendnext is actually the nextBlock of the subscriber
The signal provider can act as a signal and send signals
Subscribe first, signal later
Usage scenario: usually used instead of agent / notification
1.RACSubject simple use
- (void)setRacSubject1 { //Subscribe first, signal later //1. Create signal RACSubject *subject = [RACSubject subject]; //2. subscription //Create RACSubscriber internally [subject subscribeNext:^(id _Nullable x) { NSLog(@"First subscriber--%@", x); }]; [subject subscribeNext:^(id _Nullable x) { NSLog(@"Second subscriber---%@", x); }]; //3. Send signal //Traverse all subscribers and execute nextBlock [subject sendNext:@2]; /** Print results 2018-03-17 20:18:19.782119+0800 ReactiveObjc[23883:1420936] First subscriber -- 2 2018-03-17 20:18:19.784715+0800 ReactiveObjc[23883:1420936] Second subscriber -- 2 */ }
RACSubject underlying implementation is different from RACSignal
- The subscribeNext subscription signal is called to save the subscriber, and the subscriber's nextBlock has been assigned.
- Call sendNext to send signals, traverse all subscribers just saved, and call the next block of subscribers one by one
2. RACReplaySubject easy to use
- Provide signal class repeatedly, subclass of RACSubject
- Send signal first, then subscribe signal;
- Usage scenario:
- If every time a signal is subscribed, the previous value needs to be sent again, and the signal class needs to be provided repeatedly.
- You can set the number of capacity to limit the number of cached values, that is, only the latest values can be cached
- (void)setReplaySubject { //Create signal RACReplaySubject *replySub = [RACReplaySubject subject]; //Sending signal [replySub sendNext:@23]; [replySub sendNext:@34]; //Subscription signal // Traversal value, let a subscriber send multiple values // As long as you subscribe once, all the previously sent values can be obtained [replySub subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }]; /** 2018-03-19 12:01:14.112253+0800 ReactiveObjc[5130:446958] 23 2018-03-19 12:01:14.112511+0800 ReactiveObjc[5130:446958] 34 */ }
The underlying implementation of RACReplaySubject
- When subscribing to the signal, the subscriber and the subscriber response block are saved internally
- When the sender traverses the subscriber, call the subscriber's nextBlock
- The transmitted signal will be saved. When the subscriber subscribes to the signal, the previously saved signal will be applied to the new subscriber one by one. The capacity of the saved signal is determined by capacity, which is different from RACSubject
3. Substitute agent / notice
Here we imagine a reverse value transfer scenario. There is a custom View in vc. When you click the View, change the background color of vc
Usually we use proxy / notification / block
3.1. Let's see the simple use of agent
Set protocol in custom View
#import <UIKit/UIKit.h> @class SubjectView; @protocol SubjectDelegate <NSObject> @optional - (void)viewWithTap:(SubjectView *)subView; @end @interface SubjectView : UIView @property (nonatomic, weak) id<SubjectDelegate> delegate; @end
In vc, follow the proxy and implement the proxy method
///Agent method -(void)viewWithTap:(SubjectView *)subView{ NSLog(@"Completion of agency, Click. view"); UIColor *color = [UIColor colorWithRed:(arc4random() % 255) / 255.0 green:(arc4random() % 255) / 255.0 blue:(arc4random() % 255) / 255.0 alpha:1.0]; self.view.backgroundColor = color; }
3.2 RACSubject replaces agent
In the custom SubjectView.h file
#import <UIKit/UIKit.h> #import <ReactiveObjC.h> @interface SubjectView : UIView @property (nonatomic, strong) RACSubject *subject; @end
In the custom SubjectView.m file
#import "SubjectView.h" @implementation SubjectView - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //Sending signal [_subject sendNext:self]; } @end
Let's take a look at the operation in vc
- (void)setupSubjectView { SubjectView *subV = [[SubjectView alloc]init]; subV.backgroundColor = [UIColor redColor]; subV.frame = CGRectMake(100, 100, 100, 100); RACSubject *subject = [RACSubject subject]; [subject subscribeNext:^(id _Nullable x) { NSLog(@"Completion of agency, Click. view"); UIColor *color = [UIColor colorWithRed:(arc4random() % 255) / 255.0 green:(arc4random() % 255) / 255.0 blue:(arc4random() % 255) / 255.0 alpha:1.0]; self.view.backgroundColor = color; }]; subV.subject = subject; [self.view addSubview:subV]; }
Reference article https://www.jianshu.com/p/06218c533bde
A Fei young man Published 35 original articles, won praise 5, visited 7087 Private letter follow