iOS:RAC進(jìn)階之冷排坑

前文:http://www.itdecent.cn/p/6348148643f6

上篇文章說到了冷熱信號和副作用, 但是副作用針對與冷信號有什么影響,本文將進(jìn)行排坑。

如下:

  @weakify(self);
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSLog(@"執(zhí)行");
        @strongify(self)
        
        [self.companyApi getCollectionWithCompanyID:1 success:^(id response) {
            [subscriber sendNext:response];
            [subscriber sendCompleted];
        } fail:^(NSInteger httpCode, NSString *msg) {
            [subscriber sendError:[NSError errorWithDomain:@"HttpFail" code:httpCode userInfo:@{@"msg":msg}]];
            [subscriber sendCompleted];
        }];
        return [RACDisposable disposableWithBlock:^{
            [self.companyApi cancelRequest];
        }];
    }];
    
    RACSignal *collectionBtnSignal = [signal map:^id(id value) {
        if ([value[@"status"] integerValue]) {
            return @(YES);
        } else {
            return @(NO);
        }
    }];
    RACSignal *collectionLSignal = [signal map:^id(id value) {
        if ([value[@"status"] integerValue]) {
            return @"收藏";
        } else {
            return @"未收藏";
        }
    }];

    RAC(collectionL, text) = collectionLSignal;
    RAC(collectionBtn, selected) = collectionBtnSignal;

如果復(fù)制代碼看現(xiàn)象的話會注意到signal被訂閱了兩次,也被執(zhí)行了兩次,一個label和一個btn都同時變相的訂閱了signal。而我們知道冷信號的特點(diǎn)就是無論什么時間訂閱,訂閱者都會發(fā)送之前的消息,這也導(dǎo)致了增加了網(wǎng)絡(luò)請求次數(shù)。也許這個例子也許你已經(jīng)用到了只不過你還沒有發(fā)現(xiàn),有可能在你們的二級列表塞選中,多個組件用到同一個接口,如果利用冷信號請求數(shù)據(jù),那就會出現(xiàn)這樣的影響。(熱信號與副作用沒有影響,主要針對與他們的觸發(fā)時機(jī))

冷排坑

上面說到了副作用對于冷信號的影響,那么接下來說下怎么修改如上的代碼吧。

替換冷信號

開發(fā)人員可以利用如信號進(jìn)行請求,RACSubject及其子類RACReplaySubject來進(jìn)行請求通知。或者利用RACCommand來進(jìn)行,后邊會說到用法

利用RACSignal的Category(Operations)

RAC框架給出了5種方法進(jìn)行冷信號轉(zhuǎn)換熱信號,從本質(zhì)上講,其實(shí)按道理來說都是替換。五種方法如下:

/// Creates and returns a multicast connection. This allows you to share a single
/// subscription to the underlying signal.
- (RACMulticastConnection *)publish;

/// Creates and returns a multicast connection that pushes values into the given
/// subject. This allows you to share a single subscription to the underlying
/// signal.
- (RACMulticastConnection *)multicast:(RACSubject *)subject;

/// Multicasts the signal to a RACReplaySubject of unlimited capacity, and
/// immediately connects to the resulting RACMulticastConnection.
///
/// Returns the connected, multicasted signal.
- (RACSignal *)replay;

/// Multicasts the signal to a RACReplaySubject of capacity 1, and immediately
/// connects to the resulting RACMulticastConnection.
///
/// Returns the connected, multicasted signal.
- (RACSignal *)replayLast;

/// Multicasts the signal to a RACReplaySubject of unlimited capacity, and
/// lazily connects to the resulting RACMulticastConnection.
///
/// This means the returned signal will subscribe to the multicasted signal only
/// when the former receives its first subscription.
///
/// Returns the lazily connected, multicasted signal.
- (RACSignal *)replayLazily;

其中核心方法是- (RACMulticastConnection *)multicast:(RACSubject *)subject; 其余都是對于這個方法的封裝。接下來我們看下他的使用和實(shí)現(xiàn)。
使用方法就簡單介紹了,通過multicast創(chuàng)建一個RACMulticastConnection信號連接類,實(shí)現(xiàn)訂閱在連接還是先連接在訂閱取決與你們的業(yè)務(wù)也取決于multicast傳入的信號類型,假如傳入RACSubject那么和RACSubject用法一樣只能接受到訂閱以后的信號,如傳入RACReplaySubject則支持接受所有信號

RACMulticastConnection *connection = [signal multicast:[RACSubject subject]];
    [connection.signal subscribeNext:^(id x) {
        NSLog(@"+base:%@", x);
    }];
    [connection connect];

下面我們分析下源碼的實(shí)現(xiàn)

Category的方法
- (RACMulticastConnection *)multicast:(RACSubject *)subject {
    [subject setNameWithFormat:@"[%@] -multicast: %@", self.name, subject.name];
    RACMulticastConnection *connection = [[RACMulticastConnection alloc] initWithSourceSignal:self subject:subject];
    return connection;
}

RACMulticastConnection:
- (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;
}

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

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

    return self.serialDisposable;
}
  1. 該方法創(chuàng)建了一個RACMulticastConnection對象,并給了本身信 號和傳遞信號一個別名。
  2. 將RACMulticastConnection的兩個屬性指向連接的兩個信號。
  3. 將后傳入的信號作為原始的信號(也就是那個冷信號signal)的訂閱者。

可能是第三步有些繞,在我們的印象里訂閱者專屬于一個類:RACSubscriber,但實(shí)際上訂閱者只是一個遵守id<RACSubscriber>的對象即可,這里也可以看到面向協(xié)議編程的靈活性。subject父子都是簽署了RACSubscriber協(xié)議所以他們都可以作為訂閱者。

連接信號類如何替換的信號?

其實(shí)使整體替換掉了原有的冷信號。connect方法好比是冷信號的訂閱方法,產(chǎn)生了回掉,而冷信號創(chuàng)建回掉block的方法實(shí)際傳入的是熱信號,熱信號中必然包含了多個訂閱者的數(shù)組。而連接類實(shí)際訂閱的是熱信號的訂閱。一系列操作下來其實(shí)連接類本質(zhì)就是熱信號替換了你的冷信號。

其余的方法

// 外部引用RACMulticastConnection使用Racsubject替換冷信號
- (RACMulticastConnection *)publish;
// 內(nèi)部引用RACMulticastConnection使用RacReplaySubject替換冷信號,并對冷信號訂閱
- (RACSignal *)replay;
// 內(nèi)部引用RACMulticastConnection使用RacReplaySubject替換冷信號,只回掉最后一個信號
- (RACSignal *)replayLast;
// 本質(zhì)與replay無區(qū)別,只有使用替換熱信號訂閱時的時候才會調(diào)用connect方法
- (RACSignal *)replayLazily;

最后

也許你在日常的開發(fā)過程中沒有遇見冷熱信號的問題,但是通過這兩篇文章的閱讀可以讓你對rac框架面向協(xié)議編程和一些方法的靈活使用,如果自己寫一大部分偽代碼更能對于這個框架進(jìn)行深入的學(xué)習(xí)。最后如果你碰見你點(diǎn)擊一個收藏按鈕出現(xiàn)了收藏兩次或n次的問題,請記住rac框架的冷熱信號進(jìn)行深入處理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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