Racsignal's subscription in depth

Source: Internet
Author: User

Reactivecocoa is a FRP idea in the objective-c of the implementation of the framework, is now widely used in the United States Regiment project. For the basic use of Reactivecocoa, there are a lot of relevant information on the Internet, this article is no longer discussed. Racsignal is a very important concept in Reactivecocoa, and this paper mainly focuses on the implementation principle of racsignal. Before reading, you need to master the basic usage of racsignal.

This article mainly contains 2 parts, the first part mainly analyzes the subscription process of racsignal, the second part is the depth of the first half, On the basis of the subscription process, it is difficult to understand the two operations in Reactivecocoa: Multicast && replay.
PS: To be clear, we'll discuss next only, not error and completed, which are similar to next. This article is based on the Reactivecocoa 2.x version.
Let's first analyze the subscription process of racsignal.

#RACSignal的常见用法

-(RACSignal *)signInSignal {// part 1:[RACSignal createSignal]来获得signal  return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {    [self.signInService     signInWithUsername:self.usernameTextField.text     password:self.passwordTextField.text     complete:^(BOOL success) {    // part 3: 进入didSubscribe,通过[subscriber sendNext:]来执行next block       [subscriber sendNext:@(success)];       [subscriber sendCompleted];     }];    return nil;  }];}// part 2 : [signal subscribeNext:]来获得subscriber,然后进行subscription[[self signInSignal] subscribeNext:^(id x) {     NSLog(@"Sign in result: %@", x); }];

#Subscription过程概括
Racsignal's subscription process can be summed up in three steps:

    1. [Racsignal createsignal] to get signal
    2. [Signal Subscribenext:] To get subscriber, and then to subscription
    3. Enter Didsubscribe and execute next block by [Subscriber Sendnext:]
Step one: [racsignal createsignal] to get signal
RACSignal.m中:+ ( RACSignal *)createSignal:( RACDisposable * (^)( id < RACSubscriber > subscriber))didSubscribe {  return [ RACDynamicSignal   createSignal :didSubscribe];}RACDynamicSignal.m中+ ( RACSignal *)createSignal:( RACDisposable * (^)( id < RACSubscriber > subscriber))didSubscribe {  RACDynamicSignal *signal = [[ self   alloc ] init ]; signal-> _didSubscribe = [didSubscribe copy ];  return [signal setNameWithFormat : @"+createSignal:" ];}

[Racsignal Createsignal] calls the createsignal of the subclass racdynamicsignal to return a signal and save the block behind signal in Didsubscribe

Step two: [Signal subscribenext:] To get subscriber, and then to subscription
RACSIGNAL.M:-(Racdisposable *) Subscribenext: (void (^) (id x)) nextblock {Racsubscriber *o = [Racsubscriber SUBSCRI  Berwithnext:nextblock Error:null Completed:null]; return [self subscribe:o];} RACSUBSCRIBER.M: + (Instancetype) Subscriberwithnext: (void (^) (id x)) Next error: (void (^) (nserror *error)) Error compl Eted: (void (^) (void)) completed {Racsubscriber *subscriber = [[Self alloc] init]; subscriber-> _next = [Next C Opy]; subscriber-> _error = [error copy];  subscriber-> _completed = [completed copy]; return subscriber;} RACDYNAMICSIGNAL.M:-(racdisposable *) Subscribe: (id<racsubscriber>) Subscriber {raccompounddisposable *    disposable = [raccompounddisposable compounddisposable];    subscriber = [[Racpassthroughsubscriber alloc] Initwithsubscriber:subscriber signal:self disposable:disposable]; if (self.didsubscribe! = NULL) {racdisposable *schedulingdisposable = [Racscheduler.subscriptionscheduler Schedule : ^{RACDIsposable *innerdisposable = self.didsubscribe (subscriber);        [Disposable adddisposable:innerdisposable];        }];    [Disposable adddisposable:schedulingdisposable]; } return disposable;}
    1. [Signal Subscribenext] First obtains a subscriber, this Subscriber saved Nextblock, Errorblock, Completedblock
    2. Since this signal is actually a racdynamicsignal type, this [self subscribe] method invokes the Didsubscribe saved in step one, and the parameter is 1 in subscriber
Step three: Enter Didsubscribe, through [subscriber Sendnext:] to execute next block
RACSubscriber.m中:- (void)sendNext:(id)value {    @synchronized (self) {        void (^nextBlock)(id) = [self.next copy];        if (nextBlock == nil) return;        nextBlock(value);    }}

Any time this [subscriber Sendnext:], call Nextblock directly

Review of the subscription process of signal

From the three steps above, we see:

    • The first two calls, Createsignal and Subscribenext, are 声明 processed when value arrives in the stream
    • After the asynchronous processing of Didsubscribe block blocks is complete, the Subscriber is Sendnext,自动处理

Figure out the subscription process of RAC, and then we discuss the two operations that are more easily confused in a racsignal: multicast and replay.

Why should we know the principle of the two?
RACSignal+Operation.h中- (RACMulticastConnection *)publish;- (RACMulticastConnection *)multicast:(RACSubject *)subject;- (RACSignal *)replay;- (RACSignal *)replayLast;- (RACSignal *)replayLazily;
    • In Racsignal+operation.h, there are 5 successive definitions of the operations of the racsignal that are related to our topic, the differences between these operations are subtle, but it is easy to use the wrong words. Only after we understand the principle, do we understand the nuances of the difference between them.
    • Many times we don't realize the need to use these operations, which can cause a bug in the program because side effects executes multiple times

Application Scenarios for #multicast && replay

"Side effects occur for each subscription by default, but there is certain situations where Side effects should only OCCU R Once–for example, a network request typically should not being repeated when a new Subscriber is added. "

Refer to an example in the documentation Directory of the REACTIVECOCOA source code//This signal starts a new request on each subscription. Racsignal *networkrequest = [racsignal createsignal:^ (id<racsubscriber> subscriber) {AFHTTPRequestOperation *op eration = [Client Httprequestoperationwithrequest:request success:^ (afhttprequestoperation *operation, id re            Sponse) {[subscriber sendnext:response];        [Subscriber sendcompleted];        } failure:^ (Afhttprequestoperation *operation, Nserror *error) {[subscriber senderror:error];    }];    [Client enqueuehttprequestoperation:operation];    return [racdisposable disposablewithblock:^{[Operation Cancel]; }];}];/ /starts a single request, no matter how many subscriptions ' connection.signal '//gets. This was equivalent to The-replay operator, or similar to//+starteagerlywithscheduler:block:. Racmulticastconnection *connection = [networkrequest multicast:[racreplaysubject subject]; [Connection connect]; [ConnEction.signal subscribenext:^ (ID response) {NSLog (@ "Subscriber One:%@", response);}]; [Connection.signal subscribenext:^ (ID response) {NSLog (@ "Subscriber:%@", response);}];
    1. In the above example, if we do not have to racmulticastconnection, it will result in two network requests due to the execution of the two-time subscription.
    2. From the above example, we can see that after a signal is multicast, we subscription the connection.signal instead of the original networkrequest. This is the key to "side effects should only occur once", which we will explain later
Multicast principle Analysis

Replay is a special case for multicast, and the whole process of multicast can be split into two steps, discussed in detail below

Mechanism of the multicast Part 1:
RACMulticastConnection.m中:- (id)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject {    NSCParameterAssert(source != nil);    NSCParameterAssert(subject != nil);    self = [super init];    if (self == nil) return nil;    _sourceSignal = source;    _serialDisposable = [[RACSerialDisposable alloc] init];    _signal = subject;    return self;}
    • In combination with the above example, Racmulticastconnection's init is networkrequest as sourcesignal, and ultimately connnection.signal means [racreplaysubject Subject
RACMulticastConnection.m中:- (RACDisposable *)connect {    BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0, 1, &_hasConnected);    if (shouldConnect) {        self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal];    }    return self.serialDisposable;}
    • Combined with the subscription process of the above racsignal analysis, [self.sourcesignal subscribe:_signal] Executes self.sourcesignal didsubscribe this block. Combined with the above example, that is, will be _signal as subscriber, send the network request, success, _signal will sendnext, here this signal is [racreplaysubject subject]. As can be seen, once entered into this didsubscribe, the follow-up whether it is sendnext or subscription, are on this [racreplaysubject subject], and the original sourcesignal completely irrelevant. This explains why "side effects only occur once".
Mechanism of the multicast Part 2:

Before you proceed to step two of multicast, you need to introduce Racsubject and Racreplaysubject

---------------------annoying divider line start------------------

Racsubject

"A subject can be thought of as A signal so can manually control by sending next, completed, and error."

One usage of Racsubject is as follows:

RACSubject *letters = [RACSubject subject];// Outputs: A B[letters subscribeNext:^(id x) {    NSLog(@"%@ ", x);}];[letters sendNext:@"A"];[letters sendNext:@"B"];

Next analyze the principle of racsubject

RACSubject.m中:- (id)init {    self = [super init];    if (self == nil) return nil;    _disposable = [RACCompoundDisposable compoundDisposable];    _subscribers = [[NSMutableArray alloc] initWithCapacity:1];        return self;}
    • There is a subscribers array in the Racsubject
 racsubject.m:-(racdisposable *) Subscribe: (id<racsubscriber>) subscriber {Nscparameterassert ( Subscriber! = nil); Raccompounddisposable *disposable = [raccompounddisposable compounddisposable]; subscriber = [[Racpassthroughsubscriber alloc] Initwithsubscriber:subscriber signal:self disposable:disposable]; Nsmutablearray *subscribers = self.subscribers; @synchronized (subscribers) {[Subscribers addobject:subscriber]; } return [Racdisposable disposablewithblock:^{@synchronized (subscribers) {//Since newer Subscribe RS is generally shorter-lived, search//starting from the end of the list. Nsuinteger index = [subscribers indexofobjectwithoptions:nsenumerationreverse passingtest:^ BOOL (id<RACSubscriber > obj, nsuinteger Index, BOOL *stop) {return obj = = Subscriber; }]; if (Index! = nsnotfound) [subscribers removeobjectatindex:index]; } }];}
    • From subscribe: The implementation can be seen, the Racsubject object of each subscription, is to add this Subscriber to the subscribers array only
RACSubject.m中:- (void)sendNext:(id)value {    [self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {        [subscriber sendNext:value];    }];}
    • From the Sendnext: The realization can be seen, each Racsubject object Sendnext, will be reserved subscribers Sendnext, if this Subscriber is racsignal, The next block of signal will be executed.
Racreplaysubject

"A replay subject saves the values it is sent (up to its defined capacity) and resends those to new subscribers.", as you can see, rep Laysubject is a buffer for what it can send next (error,completed).
Racreplaysubject is inherited from the racsubject, its internal implementation such as subscribe:, Sendnext: Implementation will also invoke the implementation of super

RACReplaySubject.m中:- (instancetype)initWithCapacity:(NSUInteger)capacity {    self = [super init];    if (self == nil) return nil;    _capacity = capacity;    _valuesReceived = (capacity == RACReplaySubjectUnlimitedCapacity ? [NSMutableArray array] : [NSMutableArray arrayWithCapacity:capacity]);    return self;}
    • From Init we see that the Racreplaysubject object holds the capacity variable (used to determine how many sendnext the valuesreceived caches: The value that comes out, This is particularly useful when distinguishing between replay and Replaylast) and the valuesreceived array (used to hold Sendnext: Out of Value), which will then focus on
RACREPLAYSUBJECT.M:-(racdisposable *) Subscribe: (id<racsubscriber>) Subscriber {raccompounddisposable *    compounddisposable = [Raccompounddisposable compounddisposable];            Racdisposable *schedulingdisposable = [Racscheduler.subscriptionscheduler schedule:^{@synchronized (self) {                For (id value in self.valuesreceived) {if (compounddisposable.disposed) return;            [Subscriber Sendnext: (value = = Ractuplenil.tuplenil nil:value)];            } if (compounddisposable.disposed) return;            if (self.hascompleted) {[subscriber sendcompleted];            } else if (self.haserror) {[subscriber sendError:self.error];                } else {racdisposable *subscriptiondisposable = [Super Subscribe:subscriber];            [Compounddisposable adddisposable:subscriptiondisposable];    }        }    }];    [Compounddisposable adddisposable:schedulingdisposable]; Return Compounddisposable;} 
    • From subscribe: As can be seen, Racreplaysubject object every time subscription, will be valuesreceived in the buffer value Sendnext again, Then call super to add the current subscriber to the subscribers array.
RACReplaySubject.m中:- (void)sendNext:(id)value {    @synchronized (self) {        [self.valuesReceived addObject:value ?: RACTupleNil.tupleNil];        [super sendNext:value];        if (self.capacity != RACReplaySubjectUnlimitedCapacity && self.valuesReceived.count > self.capacity) {            [self.valuesReceived removeObjectsInRange:NSMakeRange(0, self.valuesReceived.count - self.capacity)];        }    }}

From Sendnext: As can be seen, the Racreplaysubject object will buffer each time sendnext value, then call Super, for each subscriber in subscribers, call Sendnext. The number of buffer is determined according to Self.capacity.

---------------------Annoying separation line end------------------

After the introduction of Racreplaysubject, we went on to multicast Part 2.
In the above example, we have connection.signal two times subscription, combined with the subscribe of the above Racreplaysubject subscription: We get the following process:

    1. [Racreplaysubject subject] Saves the Subscriber in the two subscription process in the subscribers array
    2. When the network request success, will [Subscriber Sendnext:response], before already said this Subscriber is [racreplaysubject subject], so, The value of Sendnext: is saved in the valuesreceived array for subsequent subscription use (I don't know if you notice Racreplaysubject's subscribe: There's A For loop), The Sendnext is then executed for each subscriber saved in subscribers.
Follow-up thinking
    1. The above discussion is Racreplaysubject object first subscription, then Sendnext, if the first sendnext, then subscription it? In fact, the charm lies in the Racreplaysubject subscribe: in the For loop. The concrete process stays for thinking
    2. In Racsignal+operation about multicast && Replay, a total of 5 operations: Publish, multicast, replay, Replaylast, replaylazily, What are the subtle differences between them? I believe that on the basis of the above content, they are not difficult to understand the nuances, here is recommended to help you understand the blog
Resources

Reactivecocoa GitHub Home
Reactivecocoa Documentation
Information on Reactivecocoa Raywenderlich

Reprinted from: http://tech.meituan.com/RACSignalSubscription.html

Racsignal's subscription in depth

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.