RAC中將冷信號(hào)轉(zhuǎn)為熱信號(hào)

我在之前一篇文章說到過冷信號(hào)帶來的問題, 有時(shí)候通過將其轉(zhuǎn)為熱信號(hào)就可以避免, 這篇文章將會(huì)介紹如何將一個(gè)冷信號(hào)轉(zhuǎn)為熱信號(hào)。

方式1: subscribe

直接上代碼吧

RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    [subscriber sendNext:@"signal"];
    return nil;
}];
RACSubject *subject = [RACSubject subject];
[subject subscribeNext:^(id  _Nullable x) {
    NSLog(@"%@",x);
}];
[signal1 subscribe:subject];  

首先RACSubject 是遵守 RACSubscriber 協(xié)議的,所以我們可以將 subject 當(dāng)作 subscriber 傳入subscribe方法中。subscripe 的實(shí)現(xiàn)如下

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCParameterAssert(subscriber != nil);

    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;
} 

可以看到 self.didSubscribe(subscriber); 這里 調(diào)用了signal 的 didSubscribe 這個(gè)block ,并且將 subscriber(也就是subject) 作為參數(shù)傳入。 看過上篇文章你就會(huì)知道,第一段代碼中block會(huì)被執(zhí)行,也就是 [subscriber sendNext:@"signal"]; 也會(huì)被執(zhí)行,這里的subscriber其實(shí)還是我們創(chuàng)建的subject,這樣的話,第一段代碼其實(shí)可以簡(jiǎn)化為

RACSubject *subject = [RACSubject subject];
[subject subscribeNext:^(id  _Nullable x) {
    NSLog(@"%@",x);
}];
[subject sendNext:@"signal"];

不過平時(shí)我們不會(huì)這么去使用,RACSignal+Operationsn 這個(gè)類別中提供了更多優(yōu)良姿勢(shì),下面一一介紹。

方式2: multicast

用法如下:

RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    [subscriber sendNext:@"signal1"];
    return nil;
}];
RACSubject *subject1 = [RACSubject subject];
RACMulticastConnection *connection = [signal1 multicast:subject1];
[connection.signal subscribeNext:^(id  _Nullable x) {
    NSLog(@"%@",x);
}];
[connection connect];

首先我們看一下 muticast 的實(shí)現(xiàn)

- (RACMulticastConnection *)multicast:(RACSubject *)subject {
    [subject setNameWithFormat:@"[%@] -multicast: %@", self.name, subject.name];
    RACMulticastConnection *connection = [[RACMulticastConnection alloc] initWithSourceSignal:self subject:subject];
    return connection;
}
- (instancetype)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject {
    NSCParameterAssert(source != nil);
    NSCParameterAssert(subject != nil);

    self = [super init];

    _sourceSignal = source;
    _serialDisposable = [[RACSerialDisposable alloc] init];
    _signal = subject;
    
    return self;
}

其實(shí)就是生成了一個(gè) RACMulticastConnection 實(shí)例,并將 signal(sourceSignal) 和 subject(signal) 分別保存起來。
所以,下面對(duì) connection.signal 的訂閱 就是對(duì)subject的訂閱。

接下來再來看一下 connect 的實(shí)現(xiàn)

- (RACDisposable *)connect {
    BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0, 1, &_hasConnected);

    if (shouldConnect) {
        self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal];
    }

    return self.serialDisposable;
}

可以看到 [self.sourceSignal subscribe:_signal]; 這里其實(shí)就是 [signal subscripe:subject] 不再多說。
這里有必要說一下 OSAtomicCompareAndSwap32Barrier
原型如下

bool OSAtomicCompareAndSwap32Barrier( int32_t __oldValue, int32_t __newValue, volatile int32_t *__theValue );

如果 __theValue 的值是 __oldValue 該方法會(huì)將 __theValue__oldValue 換為 __newValue 并且返回YES,而如果 __theValue 的值已經(jīng)是 __newValue,則不做交換并且返回NO。因此 connect 方法中 subscribe 方法只會(huì)被執(zhí)行一次。

除了connect之外,RACMulticastConnection 類中還提供了一個(gè)autoconnect方法,實(shí)現(xiàn)如下

- (RACSignal *)autoconnect {
    __block volatile int32_t subscriberCount = 0;

    return [[RACSignal
        createSignal:^(id<RACSubscriber> subscriber) {
            OSAtomicIncrement32Barrier(&subscriberCount);

            RACDisposable *subscriptionDisposable = [self.signal subscribe:subscriber];
            RACDisposable *connectionDisposable = [self connect];

            return [RACDisposable disposableWithBlock:^{
                [subscriptionDisposable dispose];

                if (OSAtomicDecrement32Barrier(&subscriberCount) == 0) {
                    [connectionDisposable dispose];
                }
            }];
        }]
        setNameWithFormat:@"[%@] -autoconnect", self.signal.name];
}

他會(huì)返回一個(gè)RACDynamicSignal ,但這個(gè)信號(hào)被訂閱時(shí)執(zhí)行connect方法,因此使用autoconnect的姿勢(shì)如下:

RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    [subscriber sendNext:@"signal1"];
    return nil;
}];
RACSubject *subject1 = [RACSubject subject];
RACMulticastConnection *connection = [signal1 multicast:subject1];
RACSignal *autoSignal = [connection autoconnect];
[autoSignal subscribeNext:^(id  _Nullable x) {
    NSLog(@"%@",x);
}];

RACMulticastConnection 用于將一個(gè)信號(hào)的訂閱分享給多個(gè)訂閱者,你需要使用 [RACSignal publish] 或者 [RACSignal multicast:] 來實(shí)例RACMulticastConnection對(duì)象,并且使用 connectautoconnect 來執(zhí)行訂閱

方式3: publish

在 RACSignal+Operations 這個(gè)類別中提供了publish方法來生成RACMulticastConnection示例,實(shí)現(xiàn)如下:

- (RACMulticastConnection *)publish {
    RACSubject *subject = [[RACSubject subject] setNameWithFormat:@"[%@] -publish", self.name];
    RACMulticastConnection *connection = [self multicast:subject];
    return connection;
}

該方法只是對(duì) multicast 方法的一個(gè)簡(jiǎn)單封裝(在內(nèi)部創(chuàng)建一個(gè)subject并對(duì)其multicast),使用 publish 更為方便快捷。使用如下:

RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
     [subscriber sendNext:@"signal1"];
     return nil;
}];
RACMulticastConnection *connection = [signal publish];
[connection.signal subscribeNext:^(id  _Nullable x) {
     NSLog(@"%@",x);
}];//相當(dāng)于 [subject subscribeNext:...]
[connection connect]; //相當(dāng)于 [subject sendNext:@"signal1"]

在 RACSignal+Operations 中除了 multicast 與 publish 方法外,還提供了 replay 、replayLast、replayLazily 這三個(gè)方法。其實(shí)他們都是對(duì) multicast 方法的封裝

方式4: replay

實(shí)現(xiàn)如下

- (RACSignal *)replay {
    RACReplaySubject *subject = [[RACReplaySubject subject] setNameWithFormat:@"[%@] -replay", self.name];

    RACMulticastConnection *connection = [self multicast:subject];
    [connection connect];

    return connection.signal;
}

publish中操作的是RACSubject并且沒有執(zhí)行connect方法然后返回的是connection實(shí)例,而replay是對(duì)RACReplaySubject的操作,并且執(zhí)行了connect方法,返回值是RACReplaySubject的實(shí)例。那么我們接受到replay的返回值其實(shí)就是拿到了由冷信號(hào)轉(zhuǎn)換得到的RACReplaySubject,由于執(zhí)行過connect方法,所以這個(gè)熱信號(hào)已經(jīng)進(jìn)行過sendnext,后續(xù)只需要對(duì)其訂閱即可觸發(fā)。使用如下:

RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
     [subscriber sendNext:@"signal1"];
     return nil;
}];
RACReplaySubject *rpSubjecxt = [signal replay]; //內(nèi)部已執(zhí)行 [rpSubjecxt sendNext:@"signal1"]
[rpSubjecxt subscribeNext:^(id  _Nullable x) {
    NSLog(@"%@",x);
}];

方式5: replayLast

- (RACSignal *)replayLast {
    RACReplaySubject *subject = [[RACReplaySubject replaySubjectWithCapacity:1] setNameWithFormat:@"[%@] -replayLast", self.name];

    RACMulticastConnection *connection = [self multicast:subject];
    [connection connect];

    return connection.signal;
}

可以看到 replayLast 的實(shí)現(xiàn)與replay想比只是在對(duì)RACReplaySubject的初始化方法,這里將RACReplaySubject中valuesReceived數(shù)組的capacity設(shè)為1, 所以當(dāng)這個(gè)RACReplaySubject被訂閱時(shí)只會(huì)執(zhí)行“在他之前的的最后一個(gè)” sendNext。 replayLast 顧名思義只會(huì)重復(fù)訂閱之前最后的一條sendNext。

方式6: replayLazily

- (RACSignal *)replayLazily {
    RACMulticastConnection *connection = [self multicast:[RACReplaySubject subject]];
    return [[RACSignal
        defer:^{
            [connection connect];
            return connection.signal;
        }]
        setNameWithFormat:@"[%@] -replayLazily", self.name];
}

和replay不同的是這里將 connect 操作 套在了 defer 方法里面(defer將RACReplaySubject用RACDynamicSignal包裹起來)。

我們來看defer的實(shí)現(xiàn)

+ (RACSignal *)defer:(RACSignal<id> * (^)(void))block {
    NSCParameterAssert(block != NULL);

    return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
        return [block() subscribe:subscriber];
    }] setNameWithFormat:@"+defer:"];
}

只有 defer 方法返回的信號(hào)被訂閱之后才會(huì)執(zhí)行 return [block() subscribe:subscriber]; ,
return [block() subscribe:subscriber]; 展開就是

[connection connect];
return [connection.signal subscripe:subscriber];

因此 replayLazily 返回的信號(hào)只有在被訂閱的時(shí)候才會(huì)執(zhí)行 [connection.signal subscripe:subscriber], replayLazily 叫做 replayLazily 再合適不過。

replayLazily 的使用如下:

RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"signal1"];
        [subscriber sendNext:@"signal2"];
        return nil;
    }];
RACSignal *rplaSignal = [signal replayLazily]; 
[rplaSignal subscribeNext:^(id  _Nullable x) {
     NSLog(@"%@",x);
}]; // rplaSignal 被訂閱時(shí) 才會(huì)執(zhí)行內(nèi)部的 `[connection.signal(RACReplaySubject) subscripe:subscriber]` 操作

總結(jié)

  1. 通過冷信號(hào)subscribe一個(gè)熱信號(hào)即可通過將熱信號(hào)當(dāng)作subscriber(訂閱者)來進(jìn)行sendNext等操作,也就是將冷信號(hào)轉(zhuǎn)為了熱信號(hào);
  2. multicast就是基于上述原理來實(shí)現(xiàn)的;
  3. publish replay replayLast replayLazily 都是基于 multicast 的封裝,滿足上的不同使用場(chǎng)景。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容