一直搞不明白replayLazily可以直接跳到最后看。
原文:http://spin.atomicobject.com/2014/06/29/replay-replaylast-replaylazily/
最近同事問我在ReactiveCoca中replay,replayLast和replayLazily有什么區(qū)別,但我對此也是一知半解,也不能完整描述出來它們之間的不同,所以,我將對它們進(jìn)行深入研究。
如果你沒有對RACReplaySubject和RACMulticastConnection有很好的理解的話,你會對它們在頭文件中的描述理解的很困難。現(xiàn)在不去了解底層原理,我會嘗試去描述解析這些方法。
對于一個“普通”的信號,每次訂閱都將會導(dǎo)致信號中的代碼再執(zhí)行一遍,且該次訂閱者僅接收到該次訂閱發(fā)送出去的值。
第一個例子演示每次訂閱都會重新執(zhí)行訂閱代碼。
__block int num = 0; RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) { num++; NSLog(@"Increment num to: %i", num); [subscriber sendNext:@(num)]; return nil; }]; NSLog(@"Start subscriptions"); // Subscriber 1 (S1) [signal subscribeNext:^(id x) { NSLog(@"S1: %@", x); }]; // Subscriber 2 (S2) [signal subscribeNext:^(id x) { NSLog(@"S2: %@", x); }]; // Subscriber 3 (S3) [signal subscribeNext:^(id x) { NSLog(@"S3: %@", x); }];
運(yùn)行結(jié)果如下:
Start subscriptions Increment num to: 1 S1: 1 Increment num to: 2 S2: 2 Increment num to: 3 S3: 3
可以看到,每次訂閱num都在遞增,如果不訂閱則不會遞增。通過這種方式,可以知道信號是懶惰的,如果沒有訂閱者的話,是不會執(zhí)行的。
第二個例子演示信號被添加訂閱的時候,訂閱者是怎么接收發(fā)送的值的。
RACSubject *letters = [RACSubject subject]; RACSignal *signal = letters; NSLog(@"Subscribe S1"); [signal subscribeNext:^(id x) { NSLog(@"S1: %@", x); }]; NSLog(@"Send A"); [letters sendNext:@"A"]; NSLog(@"Send B"); [letters sendNext:@"B"]; NSLog(@"Subscribe S2"); [signal subscribeNext:^(id x) { NSLog(@"S2: %@", x); }]; NSLog(@"Send C"); [letters sendNext:@"C"]; NSLog(@"Send D"); [letters sendNext:@"D"]; NSLog(@"Subscribe S3"); [signal subscribeNext:^(id x) { NSLog(@"S3: %@", x); }];
運(yùn)行結(jié)果
Subscribe S1 Send AS1: A Send BS1: B Subscribe S2 Send CS1: CS2: C Send DS1: DS2: D Subscribe S3
在很多情況下,這是我們想要的預(yù)期結(jié)果,不過在某些情況下,你不需要訂閱的代碼再次被執(zhí)行。例如訂閱 一個向網(wǎng)絡(luò)服務(wù)器發(fā)送的請求,當(dāng)服務(wù)器返回數(shù)據(jù)時,多個監(jiān)聽者需要更新(無論有多少個監(jiān)聽者,請求只發(fā)送一下(第一個例子就不滿足我們的需求)),或者我們想拿到訂閱前信號發(fā)送過的值(第二個例子,S2想拿A,B的值或者S3想拿A,B,C,D的值,就不滿足我們的需求了)。因此-replay
, -replayLast
, and -replayLazily應(yīng)需而生。
這個replay方法將返回一個新的信號,當(dāng)源信號被訂閱時,會立即發(fā)送給訂閱者全部歷史的值,不會重復(fù)執(zhí)行源信號中的訂閱代碼,不僅如此,訂閱者還將收到所有未來發(fā)送過去的值。
第一個例子演示信號添加新的訂閱時,代碼是不會再次被執(zhí)行的。
__block int num = 0; RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id subscriber) { num++; NSLog(@"Increment num to: %i", num); [subscriber sendNext:@(num)]; return nil; }] replay]; NSLog(@"Start subscriptions"); // Subscriber 1 (S1) [signal subscribeNext:^(id x) { NSLog(@"S1: %@", x); }]; // Subscriber 2 (S2) [signal subscribeNext:^(id x) { NSLog(@"S2: %@", x); }]; // Subscriber 3 (S3) [signal subscribeNext:^(id x) { NSLog(@"S3: %@", x); }];
Increment num to: 1 Start subscriptions S1: 1 S2: 1 S3: 1
信號首次被訂閱時,num立馬被遞增了,且僅僅遞增了一次。這說明了不管有你多個訂閱者,訂閱代碼我只執(zhí)行了一次。
第二個例子演示每個新添加的訂閱者接收到信號中全部的值(不管是之前發(fā)出的值還是將來發(fā)出的值)。
RACSubject *letters = [RACSubject subject]; RACSignal *signal = [letters replay]; NSLog(@"Subscribe S1"); [signal subscribeNext:^(id x) { NSLog(@"S1: %@", x); }]; NSLog(@"Send A"); [letters sendNext:@"A"]; NSLog(@"Send B"); [letters sendNext:@"B"]; NSLog(@"Subscribe S2"); [signal subscribeNext:^(id x) { NSLog(@"S2: %@", x); }]; NSLog(@"Send C"); [letters sendNext:@"C"]; NSLog(@"Send D"); [letters sendNext:@"D"]; NSLog(@"Subscribe S3"); [signal subscribeNext:^(id x) { NSLog(@"S3: %@", x); }];
Subscribe S1 Send A S1: A Send BS1: B Subscribe S2S2: AS2: B Send CS1: CS2: C Send DS1: DS2: D Subscribe S3S3: AS3: BS3: CS3: D
盡管訂閱者S3在所有的值發(fā)送之后再訂閱,然后還能接收到所有的值。
這個replayLast返回一個新的信號,當(dāng)源信號被訂閱時,會立即發(fā)送給訂閱者最新的值,不會重復(fù)執(zhí)行源信號中的訂閱代碼。訂閱者還會收到信號未來所有的值。
對于第一個例子,跟之前replay一樣,所以我就不再次演示了。
第二個例子演示如何將最新的值提供給新的訂閱者
RACSubject *letters = [RACSubject subject]; RACSignal *signal = [letters replayLast]; NSLog(@"Subscribe S1"); [signal subscribeNext:^(id x) { NSLog(@"S1: %@", x); }]; NSLog(@"Send A"); [letters sendNext:@"A"]; NSLog(@"Send B"); [letters sendNext:@"B"]; NSLog(@"Subscribe S2"); [signal subscribeNext:^(id x) { NSLog(@"S2: %@", x); }]; NSLog(@"Send C"); [letters sendNext:@"C"]; NSLog(@"Send D"); [letters sendNext:@"D"]; NSLog(@"Subscribe S3"); [signal subscribeNext:^(id x) { NSLog(@"S3: %@", x); }];
Subscribe S1 Send AS1: A Send BS1: B Subscribe S2S2: B Send CS1: CS2: C Send DS1: DS2: D Subscribe S3S3: D
這replayLazily方法返回一個新的信號,當(dāng)源信號被訂閱時,會立即發(fā)送給訂閱者全部歷史的值,不會重復(fù)執(zhí)行源信號中的訂閱代碼。跟replay不同的是,replayLazily被訂閱生成新的信號之前是不會對源信號進(jìn)行訂閱的(原文寫的有點(diǎn)繞,簡單來講 直到訂閱時候才真正創(chuàng)建一個信號,源信號的訂閱代碼才開始執(zhí)行)。暫時不理解也沒事,看下面的代碼輸出,和注釋。
這第一個例子會說明跟replay差異。 注意字符串 “Increment num to: 1”是被訂閱了之后才打印顯示的。而replay和replayLast沒被訂閱前就打印了“Increment num to: 1” 這個消息。
__block int num = 0; RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id subscriber) { num++; NSLog(@"Increment num to: %i", num); [subscriber sendNext:@(num)]; return nil; }] replayLazily]; //跟replay不同的就這么一個地方 NSLog(@"Start subscriptions"); // Subscriber 1 (S1) [signal subscribeNext:^(id x) { NSLog(@"S1: %@", x); }]; // Subscriber 2 (S2) [signal subscribeNext:^(id x) { NSLog(@"S2: %@", x); }]; // Subscriber 3 (S3) [signal subscribeNext:^(id x) { NSLog(@"S3: %@", x); }];
// 帖子滾動起來,跟replay比較一下 Increment num to: 1 的顯示順序。Start subscriptions // 實(shí)際訂閱Increment num to: 1 // 信號開始創(chuàng)建S1: 1S2: 1S3: 1
第二個例子演示將全部歷史的值提供給任何新的訂閱者,就像replay一樣。
RACSubject *letters = [RACSubject subject]; RACSignal *signal = [letters replayLazily]; NSLog(@"Subscribe S1"); [signal subscribeNext:^(id x) { NSLog(@"S1: %@", x); }]; NSLog(@"Send A"); [letters sendNext:@"A"]; NSLog(@"Send B"); [letters sendNext:@"B"]; NSLog(@"Subscribe S2"); [signal subscribeNext:^(id x) { NSLog(@"S2: %@", x); }]; NSLog(@"Send C"); [letters sendNext:@"C"]; NSLog(@"Send D"); [letters sendNext:@"D"]; NSLog(@"Subscribe S3"); [signal subscribeNext:^(id x) { NSLog(@"S3: %@", x); }];
Subscribe S1 Send AS1: A Send BS1: B Subscribe S2S2: AS2: B Send CS1: CS2: C Send DS1: DS2: D Subscribe S3S3: AS3: BS3: CS3: D
總結(jié)一下:
ReactiveCocoa提供了這三個簡便的方法允許多個訂閱者訂閱一個信號,卻不會重復(fù)執(zhí)行訂閱代碼,并且能給新加的訂閱者提供訂閱前的值。replay和replayLast使信號變成熱信號,且會提供所有值(-replay
) 或者最新的值(-replayLast
) 給訂閱者。 replayLazily返回一個冷的信號,會提供所有的值給訂閱者。
新聞熱點(diǎn)
疑難解答