(大喇叭:)本篇比較長(zhǎng)請(qǐng)耐心閱讀,主要解釋一下RACSignal的部分內(nèi)容赏殃,詳細(xì)介紹了RACSequence和RACTuple的原理敷待,介紹了Push-Driven和Pull-Driven。
簡(jiǎn)介
ReactiveCocoa是由git開(kāi)源的應(yīng)用于iOS和OS開(kāi)發(fā)的重型FRP (Functional Reactive Programming 是一種響應(yīng)變化的編程范式) 框架仁热。內(nèi)部使用了大量的block榜揖。FRP的核心就是信號(hào)。
RACStream
This class represents a monad, upon which many stream-based operations can be built.
RACStream
是一種流抗蠢。本身并沒(méi)有特別大的用處举哟,主要在兩個(gè)子類(lèi)RACSignal和RACSequence上得到體現(xiàn)。
由于RACStream
是一個(gè)根類(lèi)迅矛,所以暫時(shí)先不做介紹妨猩,它的一些方法主要會(huì)用在RACSignal中。先把周邊介紹一下在回過(guò)頭來(lái)看秽褒,就不會(huì)那么害怕了壶硅。
RACSignal
RACSignal
是ReactiveCocoa中的重要概念,是一種信號(hào)流销斟,繼承于RACStream
庐椒。信號(hào)就是數(shù)據(jù)流,可以用來(lái)傳遞和綁定蚂踊。
這個(gè)RACSignal
通常是冷信號(hào)(關(guān)于冷信號(hào)與熱信號(hào)之后章節(jié)中再詳細(xì)說(shuō)明)约谈。
這里引用一下這里的解釋?zhuān)杏X(jué)很形象。
使用ReactiveCocoa實(shí)現(xiàn)iOS平臺(tái)響應(yīng)式編程
把信號(hào)想象成水龍頭,只不過(guò)里面不是水窗宇,而是玻璃球(value)措伐,直徑跟水管的內(nèi)徑一樣,這樣就能抱枕玻璃球是依次排列军俊,不會(huì)出現(xiàn)并排的情況(數(shù)據(jù)都是線(xiàn)性處理的侥加,不會(huì)出現(xiàn)并發(fā)情況)。水龍頭的開(kāi)放默認(rèn)是關(guān)的粪躬,除非有了接收方(subscriber)担败,才會(huì)重新打開(kāi)。這樣只要有新的玻璃球進(jìn)來(lái)镰官,就會(huì)自動(dòng)傳送給接收方提前。
創(chuàng)建一個(gè)信號(hào) +createSignal:
通常情況下,我們使用[RACSignal createSignal:]
來(lái)創(chuàng)建信號(hào)泳唠。
信號(hào)的發(fā)送過(guò)程查看之前的文章:ReactiveCocoa信號(hào)發(fā)送詳解
RACSignal
會(huì)發(fā)送三種信號(hào)狀態(tài)狈网,sendNext:
, sendError:
笨腥, sendCompleted
拓哺。
-
sendNext
:可以發(fā)送多個(gè)next,subscriber會(huì)依次接受發(fā)送的數(shù)據(jù)脖母。 -
sendError
:發(fā)送錯(cuò)誤士鸥,為NSError
對(duì)象。 -
sendCompleted
:發(fā)送成功谆级,整個(gè)流程完成烤礁,不會(huì)再發(fā)送新的next。
在一個(gè)Racsignal的生命周期中肥照,可以發(fā)送多個(gè)next數(shù)據(jù)脚仔,但是只能發(fā)送一個(gè)Error或者Completed。
+error:
返回一個(gè)信號(hào)建峭,訂閱后玻侥,立即發(fā)送錯(cuò)誤
+never
返回一個(gè)永遠(yuǎn)不會(huì)完成的信號(hào)。
+startEagerlyWithScheduler:block:
+startLazilyWithScheduler:block:
這個(gè)方法跟下面的eagerSequence
有點(diǎn)類(lèi)似亿蒸。在沒(méi)有訂閱該信號(hào)的情況下,會(huì)執(zhí)行一次block中的內(nèi)容掌桩。這個(gè)方法通常會(huì)用在異步處理網(wǎng)絡(luò)請(qǐng)求上边锁。詳細(xì)的原理,在理解RACMulticastConnection
之后就會(huì)比較清楚了波岛。
RACSignal *signal1 = [[RACSignal startEagerlyWithScheduler:[RACScheduler schedulerWithPriority:RACSchedulerPriorityBackground] block:^(id<RACSubscriber> subscriber) {
NSLog(@"signal 1茅坛、、、贡蓖、曹鸠、、");
[subscriber sendNext:@"1"];
[subscriber sendCompleted];
}] deliverOn:[RACScheduler mainThreadScheduler]];
RACSignal *signal2 = [[RACSignal startLazilyWithScheduler:[RACScheduler schedulerWithPriority:RACSchedulerPriorityBackground] block:^(id<RACSubscriber> subscriber) {
NSLog(@"signal 2斥铺、彻桃、、晾蜘、邻眷、、剔交、");
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
}] deliverOn:[RACScheduler mainThreadScheduler]];
//運(yùn)行代碼在沒(méi)有訂閱信號(hào)的時(shí)候會(huì)有輸出,是因?yàn)樵赱RACMulticastConnection connect]中會(huì)有subscribe發(fā)生肆饶。
//ReactiveCocoaTest[8783:14103027] signal 1、岖常、驯镊、、竭鞍、板惑、
+return:
直接返回一個(gè)信號(hào),并發(fā)送一個(gè)value笼蛛。
+empty
返回一個(gè)nil
-concat:
把一個(gè)信號(hào)拼接到另一個(gè)信號(hào)后面洒放,當(dāng)多個(gè)信號(hào)發(fā)送時(shí),有序的接受信號(hào)滨砍。如果第一個(gè)信號(hào)沒(méi)有發(fā)送完成往湿,則后續(xù)的信號(hào)不會(huì)執(zhí)行。
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"signalA"];
[subscriber sendCompleted];
return nil;
}];
RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"signalB"];
return nil;
}];
// 將兩個(gè)信號(hào)拼接
RACSignal *signal = [signalA concat:signalB];
[signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 此時(shí)會(huì)輸出signalA惋戏、signalB领追。如果調(diào)換位置[signalB concat:signalA],則只會(huì)輸出signalB
<b>原理:</b>
- 創(chuàng)建了一個(gè)新的信號(hào)响逢。
- 這個(gè)新的信號(hào)內(nèi)部中绒窑,訂閱了當(dāng)前的信號(hào)singalA,依次發(fā)送數(shù)據(jù)sendNext:
- 當(dāng)發(fā)送完成,執(zhí)行completed舔亭,此時(shí)signalB會(huì)被訂閱者訂閱
- (RACSignal *)concat:(RACSignal *)signal {
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
RACSerialDisposable *serialDisposable = [[RACSerialDisposable alloc] init];
RACDisposable *sourceDisposable = [self subscribeNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
RACDisposable *concattedDisposable = [signal subscribe:subscriber];
serialDisposable.disposable = concattedDisposable;
}];
serialDisposable.disposable = sourceDisposable;
return serialDisposable;
}] setNameWithFormat:@"[%@] -concat: %@", self.name, signal];
}
-zipWith:
壓縮信號(hào)些膨,將兩個(gè)信號(hào)壓縮之后,把數(shù)據(jù)合并成一個(gè)RACTuple發(fā)送出去钦铺。
<b>原理:</b>
- (RACSignal *)zipWith:(RACSignal *)signal {
NSCParameterAssert(signal != nil);
// 創(chuàng)建一個(gè)新信號(hào)
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
__block BOOL selfCompleted = NO;
NSMutableArray *selfValues = [NSMutableArray array];
__block BOOL otherCompleted = NO;
NSMutableArray *otherValues = [NSMutableArray array];
// 發(fā)送成功
void (^sendCompletedIfNecessary)(void) = ^{
@synchronized (selfValues) {
BOOL selfEmpty = (selfCompleted && selfValues.count == 0);
BOOL otherEmpty = (otherCompleted && otherValues.count == 0);
// 兩個(gè)信號(hào)都發(fā)送成功則新信號(hào)發(fā)送成功
if (selfEmpty || otherEmpty) [subscriber sendCompleted];
}
};
// sendNext
void (^sendNext)(void) = ^{
@synchronized (selfValues) {
if (selfValues.count == 0) return;
if (otherValues.count == 0) return;
RACTuple *tuple = [RACTuple tupleWithObjects:selfValues[0], otherValues[0], nil];
[selfValues removeObjectAtIndex:0];
[otherValues removeObjectAtIndex:0];
// 當(dāng)壓縮成一個(gè)RACTuple時(shí)订雾,會(huì)將第一次發(fā)送的數(shù)據(jù)從數(shù)組中刪除
// 發(fā)送數(shù)據(jù)
[subscriber sendNext:tuple];
sendCompletedIfNecessary();
}
};
// 訂閱singalA被訂閱
RACDisposable *selfDisposable = [self subscribeNext:^(id x) {
@synchronized (selfValues) {
[selfValues addObject:x ?: RACTupleNil.tupleNil];
sendNext();
}
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
@synchronized (selfValues) {
selfCompleted = YES;
sendCompletedIfNecessary();
}
}];
// singalB訂閱
RACDisposable *otherDisposable = [signal subscribeNext:^(id x) {
@synchronized (selfValues) {
[otherValues addObject:x ?: RACTupleNil.tupleNil];
sendNext();
}
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
@synchronized (selfValues) {
otherCompleted = YES;
sendCompletedIfNecessary();
}
}];
return [RACDisposable disposableWithBlock:^{
[selfDisposable dispose];
[otherDisposable dispose];
}];
}] setNameWithFormat:@"[%@] -zipWith: %@", self.name, signal];
}
- 創(chuàng)建一個(gè)新信號(hào),singalA和singalB兩個(gè)信號(hào)都會(huì)在內(nèi)部被訂閱矛洞。
- 當(dāng)singalA發(fā)送信號(hào)洼哎,會(huì)判斷singalB有沒(méi)有發(fā)送信號(hào),同理,sigbalB發(fā)送信號(hào)時(shí)會(huì)檢測(cè)singalA有沒(méi)有發(fā)送信號(hào)噩峦,當(dāng)兩個(gè)都發(fā)送了信號(hào)之后會(huì)打包成一個(gè)RACTuple锭沟。
- sendCompleted也是同理。
訂閱 -subscribe
創(chuàng)建了signal之后识补,本身是不會(huì)做任何處理族淮,必須訂閱這個(gè)信號(hào),才能為我所用李请。
通常情況下使用[signal subscribeNext:]
直接訂閱信號(hào)瞧筛,這個(gè)方法等返回值是一個(gè)RACDisposable
類(lèi)型的值。也就是我們可以在外部對(duì)這個(gè)信號(hào)進(jìn)行操作导盅,比如取消訂閱[disposable dispose]
较幌。
// creat a signal
RACSignal *signal = [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"a"];
[subscriber sendCompleted];
// 取消訂閱要調(diào)用這里block塊
return [RACDisposable disposableWithBlock:^{
NSLog(@"disposed");
}];
}] doNext:^(id x) {
NSLog(@"do next");
}] doCompleted:^{
NSLog(@"do Completed");
}];
// subscribe do subscription
[signal subscribeNext:^(id x) {
// subscription
NSLog(@"subscriber receive value: %@", x);
} completed:^{
NSLog(@"completed end");
}];
doNext:、doCompleted白翻、doError:
此處只用doNext:
來(lái)做說(shuō)明乍炉,其它兩個(gè)與此類(lèi)似。
doNext:
是在執(zhí)行sendNext:
之前執(zhí)行的操作滤馍,就是訂閱成功之后岛琼,將要發(fā)送訂閱數(shù)據(jù)時(shí)會(huì)先執(zhí)行此方法。
// RACSignal+Operations.m 源碼
- (RACSignal *)doNext:(void (^)(id x))block {
NSCParameterAssert(block != NULL);
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
return [self subscribeNext:^(id x) {
// 先會(huì)掉block
block(x);
// 執(zhí)行訂閱發(fā)送
[subscriber sendNext:x];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
[subscriber sendCompleted];
}];
}] setNameWithFormat:@"[%@] -doNext:", self.name];
}
<b>注意:</b>doNext:
方法返回的事一個(gè)新的signal巢株,這是一個(gè)新創(chuàng)建的信號(hào)槐瑞,并且在[subscriber sendNext:x]
之前,首先回調(diào)了block(x)
阁苞。所以困檩,如果將doNext:
單獨(dú)拿出來(lái)帅刊,在之后的信號(hào)訂閱過(guò)程中八孝,是不會(huì)執(zhí)行doNext:
方法。因?yàn)橛嗛喌氖亲钤嫉男盘?hào)牺荠。
看一下上面代碼的打印結(jié)果骚灸,如下:
RACDemo[44640:4871628] do next
RACDemo[44640:4871628] subscriber receive value a
RACDemo[44640:4871628] do Completed
RACDemo[44640:4871628] completed end
<b>RACSiganle先說(shuō)這么多糟趾,其他的會(huì)在下一篇文章中詳細(xì)解釋。</b>
RACSequence
單純的翻譯來(lái)看就是<b>序列</b>甚牲。那這個(gè)是用來(lái)干嘛的呢义郑?
Represents an immutable sequence of values. Unless otherwise specified, the sequences' values are evaluated lazily on demand. Like Cocoa collections, sequences cannot contain nil.
官方是這么解釋的,通俗的翻譯一下就是:
RACSequence
代表的是一組不可變的序列值丈钙。除非另有說(shuō)明魔慷,這個(gè)序列值在使用的時(shí)候通過(guò)懶加載的方式。跟cocoa框架中的集合一樣著恩,不能包含空值。
想到這里,就會(huì)想到Foundation中的NSArray
喉誊,NSDictionary
邀摆。不錯(cuò),這里可以將NSArray
伍茄,NSDictionary
轉(zhuǎn)換為RACSequence
栋盹。并且通過(guò)RACSequence
轉(zhuǎn)為RACSignal
。
<b>NSArray轉(zhuǎn)換 --> RACArraySequence</b>
NSArray *array = @[@"a", @"b", @"c"];
// 可以調(diào)用array.rac_sequence直接做轉(zhuǎn)換
RACSequence *arraySeq = array.rac_sequence;
RACSignal *arraySignal = arraySeq.signal;
[arraySignal subscribeNext:^(id x) {
NSLog(@"subscriber array receive value: %@",x);
}];
源碼
// NSArray+RACSequenceAdditions.m
- (RACSequence *)rac_sequence {
return [RACArraySequence sequenceWithArray:self offset:0];
}
// RACArraySequence.m
+ (instancetype)sequenceWithArray:(NSArray *)array offset:(NSUInteger)offset {
NSCParameterAssert(offset <= array.count);
if (offset == array.count) return self.empty;
RACArraySequence *seq = [[self alloc] init];
seq->_backingArray = [array copy];
seq->_offset = offset;
return seq;
}
上述源碼中提到了RACArraySequence
敷矫,調(diào)用了一個(gè)類(lèi)方法直接返回RACArraySequence
例获。
這個(gè)RACArraySequence
中有兩個(gè)屬性分別存儲(chǔ)數(shù)組和偏移。這個(gè)offset
值得一提曹仗,代表的是這個(gè)Sequence
是從array
的第幾個(gè)開(kāi)始的榨汤。接下來(lái)就會(huì)看到。往下看
// 獲取頭
- (id)head {
return self.backingArray[self.offset];
}
// 獲取尾部
- (RACSequence *)tail {
RACSequence *sequence = [self.class sequenceWithArray:self.backingArray offset:self.offset + 1];
sequence.name = self.name;
return sequence;
}
// 獲取數(shù)組
- (NSArray *)array {
return [self.backingArray subarrayWithRange:NSMakeRange(self.offset, self.backingArray.count - self.offset)];
}
這個(gè)head返回的值是數(shù)組中第offset
個(gè)數(shù)據(jù)怎茫。返回的array是從第offset個(gè)開(kāi)始的收壕。
而tail
這個(gè)方法返回的還是一個(gè)序列,而且這個(gè)序列是從第offset+1個(gè)數(shù)據(jù)開(kāi)始的轨蛤,有可能回返回empty蜜宪。
以上的三個(gè)方法會(huì)在RACSequence
中詳細(xì)介紹。
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained _Nullable [_Nonnull])buffer count:(NSUInteger)len;
這個(gè)方法是NSFastEnumeration
協(xié)議中的方法祥山,不做顯式調(diào)用圃验。在這里就不去追究了。
<b>NSDictionary轉(zhuǎn)換</b>
NSDictionary *dict = @{@"key1" : @"value1",
@"key2" : @"value2"};
RACSequence *dictSeq = dict.rac_sequence;
RACSignal *dictSignal = dictSeq.signal;
// 字典中的數(shù)據(jù)流比較特殊缝呕、是一個(gè)`RACTuple`類(lèi)型澳窑,下面會(huì)講到。
[dictSignal subscribeNext:^(RACTuple *tupe) {
NSLog(@"key = %@, value = %@", tupe.first, tupe.second);
}];
// 所有的key的序列
RACSequence *keysSeq = dict.rac_keySequence;
// 所有的value的序列
RACSequence *valuesSeq = dict.rac_valueSequence;
- (RACSequence *)rac_sequence {
NSDictionary *immutableDict = [self copy];
// TODO: First class support for dictionary sequences.
return [immutableDict.allKeys.rac_sequence map:^(id key) {
id value = immutableDict[key];
return RACTuplePack(key, value);
}];
}
源碼中可以看到是通過(guò)獲取dictionary所有key來(lái)重新組成一個(gè)新的序列岳颇。主要的就是一個(gè)map函數(shù)照捡,不要著急,馬上就會(huì)介紹话侧。
RACSequence的方法
這個(gè)東西栗精,真的是很牛x的,感慨一下瞻鹏。源文件就不貼了悲立,自己看吧,一個(gè)一個(gè)介紹新博。
head薪夕、tail、array
// 返回第一個(gè)序列中的第一個(gè)數(shù)據(jù)
@property (nonatomic, strong, readonly) id head;
// 返回除了第一個(gè)數(shù)據(jù)之外的其他數(shù)據(jù)組成的序列
@property (nonatomic, strong, readonly) RACSequence *tail;
// 返回一個(gè)數(shù)組
@property (nonatomic, copy, readonly) NSArray *array;
這幾個(gè)屬性就不說(shuō)了赫悄,上面已經(jīng)介紹過(guò)了原献。
objectEnumerator
// RACSequence.h
@property (nonatomic, copy, readonly) NSEnumerator *objectEnumerator;
// RACSequence.m
- (NSEnumerator *)objectEnumerator {
RACSequenceEnumerator *enumerator = [[RACSequenceEnumerator alloc] init];
enumerator.sequence = self;
return enumerator;
}
這個(gè)objectEnumerator
其實(shí)是一個(gè)RACSequenceEnumerator馏慨,是一個(gè)遍歷,來(lái)自NSEnumerator
姑隅。
-eagerSequence写隶、-lazySequence
/// Converts a sequence into an eager sequence.
///
/// An eager sequence fully evaluates all of its values immediately. Sequences
/// derived from an eager sequence will also be eager.
///
/// Returns a new eager sequence, or the receiver if the sequence is already
/// eager.
@property (nonatomic, copy, readonly) RACSequence *eagerSequence;
/// Converts a sequence into a lazy sequence.
///
/// A lazy sequence evaluates its values on demand, as they are accessed.
/// Sequences derived from a lazy sequence will also be lazy.
///
/// Returns a new lazy sequence, or the receiver if the sequence is already lazy.
@property (nonatomic, copy, readonly) RACSequence *lazySequence;
這里需要詳細(xì)解釋一下。
在一開(kāi)始提到RACSequence
的時(shí)候讲仰,我們就已經(jīng)說(shuō)過(guò):<b>除非另有說(shuō)明慕趴,這個(gè)序列值在使用的時(shí)候通過(guò)懶加載的方式。</b>而eagerSequence
則是lazy的對(duì)立面鄙陡。
// 通過(guò)RACEagerSequence返回
- (RACSequence *)eagerSequence {
return [RACEagerSequence sequenceWithArray:self.array offset:0];
}
// 返回自己
- (RACSequence *)lazySequence {
return self;
}
我們先不看其原理是怎么樣的冕房,我們先來(lái)看個(gè)例子。原理在下一章會(huì)講趁矾。
NSArray *array1 = @[@"a", @"b", @"c"];
NSArray *array2 = @[@1, @2, @3];
// 這里又用到了map函數(shù)(映射)耙册,馬上就會(huì)講到
RACSequence *seq1 = [array1.rac_sequence map:^id(id value) {
NSLog(@"lazy value = %@", value);
return value;
}];
RACSequence *seq2 = [array2.rac_sequence.eagerSequence map:^id(id value) {
NSLog(@"eager value = %@", value);
return value;
}];
NSArray *newArray1 = seq1.array;
NSArray *newArray2 = seq2.array;
// 打印結(jié)果
RACDemo[26685:1927997] eager value = 1
RACDemo[26685:1927997] eager value = 2
RACDemo[26685:1927997] eager value = 3
RACDemo[26685:1927997] lazy value = a
RACDemo[26685:1927997] lazy value = b
RACDemo[26685:1927997] lazy value = c
從打印結(jié)果來(lái)看,先打印的eager愈魏,然后才是lazy觅玻。在這里即使將后兩行代碼注釋掉,也會(huì)執(zhí)行eager里的NSLog培漏。先劇透一下原理溪厘,其實(shí)就是bind
,大家可以先了解一下牌柄。
-signal畸悬、-signalWithScheduler:
- (RACSignal *)signal;
- (RACSignal *)signalWithScheduler:(RACScheduler *)scheduler;
將RACSequence
轉(zhuǎn)為RACSignal
。
RACScheduler
在合適的時(shí)候會(huì)說(shuō)一下這個(gè)類(lèi)珊佣,內(nèi)部使用GCD實(shí)現(xiàn)的蹋宦。
-foldLeftWithStart:reduce:、-foldRightWithStart:reduce
- (id)foldLeftWithStart:(id)start reduce:(id (^)(id accumulator, id value))reduce;
- (id)foldRightWithStart:(id)start reduce:(id (^)(id first, RACSequence *rest))reduce;
這兩個(gè)方法都是遍歷咒锻、給一個(gè)開(kāi)始的值冷冗,一個(gè)是從左開(kāi)始遍歷,一個(gè)是從又開(kāi)始遍歷惑艇。簡(jiǎn)化方法就是:
[1,2,3] startValue
left: reduce(reduce(reduce(startValue,1),2),3)
right reduce(reduce(reduce(1,startValue),2),3)
舉個(gè)??吧蒿辙,看的更真切。
NSArray *array1 = @[@"a", @"b", @"c"];
NSArray *array2 = @[@1, @2, @3];
id a = [array1.rac_sequence foldLeftWithStart:@"d" reduce:^id(id accumulator, id value) {
NSLog(@"accumulator = %@, value = %@", accumulator, value);
return [NSString stringWithFormat:@"%@ + %@",accumulator, value];
}];
NSLog(@"a = %@", a);
id b = [array2.rac_sequence foldRightWithStart:@(4) reduce:^id(id first, RACSequence *rest) {
NSLog(@"first = %@, rest.head = %@",first, rest.head);
return @([first integerValue] + [rest.head integerValue]);
}];
NSLog(@"b = %@", b);
// 打印結(jié)果:
RACDemo[27480:1956629] accumulator = d, value = a
RACDemo[27480:1956629] accumulator = d + a, value = b
RACDemo[27480:1956629] accumulator = d + a + b, value = c
RACDemo[27480:1956629] a = d + a + b + c
RACDemo[27480:1956629] first = 3, rest.head = 4
RACDemo[27480:1956629] first = 2, rest.head = 7
RACDemo[27480:1956629] first = 1, rest.head = 9
RACDemo[27480:1956629] b = 10
-any滨巴、-all
- (BOOL)any:(BOOL (^)(id value))block;
- (BOOL)all:(BOOL (^)(id value))block;
這兩個(gè)都是測(cè)試型函數(shù),比較簡(jiǎn)單思灌。
- any是序列中的任一一個(gè)元素滿(mǎn)足某個(gè)條件為true。
- all則是序列中的所有元素都滿(mǎn)足某一條件返回true恭取。內(nèi)部調(diào)用
foldLeftWithStart:
一個(gè)一個(gè)比較泰偿。這里感覺(jué)不太好,如果有一個(gè)不滿(mǎn)足就應(yīng)該直接返回BOOL值蜈垮,剩下的元素就不應(yīng)該再比較了耗跛。浪費(fèi)資源啊裕照。
-objectPassingTest:
這個(gè)函數(shù)是在any:
函數(shù)中調(diào)用的。內(nèi)部調(diào)用了flatmap:
课兄。滿(mǎn)足條件的記錄下來(lái)牍氛,不滿(mǎn)足條件的過(guò)濾掉,如果有多個(gè)數(shù)據(jù)滿(mǎn)足條件烟阐,返回第一個(gè)。
- (id)objectPassingTest:(BOOL (^)(id value))block;
+sequenceWithHeadBlock:tailBlock:
+ (RACSequence *)sequenceWithHeadBlock:(id (^)(void))headBlock tailBlock:(RACSequence *(^)(void))tailBlock;
這個(gè)就更好理解了紊扬,這是一個(gè)類(lèi)方法蜒茄,通過(guò)我們之前了解到的head
和tail
大概也能知道,就是自己定義head和tail組合成一個(gè)新的RACSequence
餐屎。
RACTupleNil
在RACTuple.h
中有一個(gè)RACTupleNil
類(lèi)檀葛,這個(gè)類(lèi)就是為了表示一個(gè)null的元素。也就是說(shuō)RACTuple
的元素中有null則用RACTupleNil
表示腹缩。
在RACTuple
中屿聋,用RACTupleNil
來(lái)表示元組中沒(méi)有內(nèi)容。
RACTupleNil
是一個(gè)單例藏鹊,用RACTupleNil.tupleNil來(lái)表示润讥。
不知道設(shè)計(jì)這個(gè)類(lèi)的目的是什么?
RACTuple
這是一個(gè)遵循NSCoding, NSCopying, NSFastEnumeration
協(xié)議的繼承自NSObject
的有序的序列盘寡。
屬性
-
count
tuple中的元素個(gè)數(shù) -
frist
tuple中第一個(gè)元素楚殿,second
/third
/fourth
/fifth
-
last
最后一個(gè)元素
這幾個(gè)屬性是只讀,獲取當(dāng)前tuple中的第幾個(gè)元素竿痰。當(dāng)index>count時(shí)脆粥,返回nil。
在RACTuple.m
中影涉,我們看看RACTuple
的具體實(shí)現(xiàn)
@interface RACTuple ()
@property (nonatomic, strong) NSArray *backingArray;
@end
這里有一個(gè)數(shù)組变隔,用來(lái)表示元組中的元素。是一個(gè)不可變的數(shù)組
#pragma mark NSCopying
- (instancetype)copyWithZone:(NSZone *)zone {
// we're immutable, bitches!
return self;
}
上面的源代碼中有一句作者的注釋?zhuān)f(shuō)這是一個(gè)不可變的蟹倾,也就是說(shuō)元組是不可變的匣缘。
+tupleWithObjectsFromArray:
將數(shù)組轉(zhuǎn)換成tuple,在實(shí)現(xiàn)中調(diào)用的是下面的方法喊式。
+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array {
return [self tupleWithObjectsFromArray:array convertNullsToNils:NO];
}
+tupleWithObjectsFromArray:convertNullsToNils:
將數(shù)組轉(zhuǎn)換為tuple孵户,如果convert
是YES
則將NSNUll
轉(zhuǎn)換為RACTupleNil
RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:@[@(1), @(2), [NSNull null]]];
NSLog(@"%@", tuple);
RACTuple *tuple1 = [RACTuple tupleWithObjectsFromArray:@[@(1), @(2), [NSNull null]] convertNullsToNils:YES];
NSLog(@"%@", tuple1);
在這里我們發(fā)現(xiàn)這兩個(gè)輸出顯示的元組中的元素是一樣的。
ReactiveCocoaTest[40628:6604925] <RACTuple: 0x600000005ea0> (
1,
2,
"<null>"
)
ReactiveCocoaTest[40628:6604925] <RACTuple: 0x608000005c00> (
1,
2,
"<null>"
)
那這兩個(gè)到底有什么區(qū)別岔留?
+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array convertNullsToNils:(BOOL)convert {
RACTuple *tuple = [[self alloc] init];
if (convert) {
NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count];
for (id object in array) {
[newArray addObject:(object == NSNull.null ? RACTupleNil.tupleNil : object)];
}
tuple.backingArray = newArray;
} else {
tuple.backingArray = [array copy];
}
return tuple;
}
從上面的源碼中可以看到夏哭,當(dāng)convert == YES
時(shí),會(huì)將NSNull.null
轉(zhuǎn)為RACTupleNil.tupleNil
献联。存儲(chǔ)的數(shù)據(jù)發(fā)生了變化竖配。在控制臺(tái)輸出時(shí)何址,會(huì)調(diào)用[class description]
方法。
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p> %@", self.class, self, self.allObjects];
//這里打印的是self.allObjects
}
allObjects
是一個(gè)數(shù)組进胯,在其內(nèi)部又會(huì)將RACTupleNil.tupleNil
轉(zhuǎn)換為NSNull.null
用爪,所以控制臺(tái)的輸出結(jié)果是一樣的。
- (NSArray *)allObjects {
NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:self.backingArray.count];
for (id object in self.backingArray) {
[newArray addObject:(object == RACTupleNil.tupleNil ? NSNull.null : object)];
}
return newArray;
}
-tupleWithObjects:
類(lèi)似于數(shù)組的初始化方法胁镐,將數(shù)據(jù)轉(zhuǎn)為tuple
-objectAtIndex:
獲取當(dāng)前index
的元素偎血,如果index > count
則返回nil。
-allObjects
獲取tuple中的所有元素盯漂,組成一個(gè)數(shù)組颇玷。
-tupleByAddingObject:
類(lèi)似于可變數(shù)組,將一個(gè)元素拼接在tuple的元素后面并返回一個(gè)新的tuple就缆。
RACTupleUnpackingTrampoline
在來(lái)說(shuō)說(shuō)這個(gè)類(lèi)帖渠,看到的時(shí)候是一臉懵逼,這個(gè)是干么的竭宰。連個(gè)注釋都沒(méi)有...
兩個(gè)方法空郊,一個(gè)是單例,就不說(shuō)了切揭,另一個(gè)看代碼
- (void)setObject:(RACTuple *)tuple forKeyedSubscript:(NSArray *)variables {
NSCParameterAssert(variables != nil);
[variables enumerateObjectsUsingBlock:^(NSValue *value, NSUInteger index, BOOL *stop) {
__strong id *ptr = (__strong id *)value.pointerValue;
*ptr = tuple[index];
}];
}
這個(gè)方法傳進(jìn)來(lái)兩個(gè)值狞甚,一個(gè)是tuple,一個(gè)是array(注意:數(shù)組中的元素是NSValue)
遍歷數(shù)組伴箩,取數(shù)組的每一個(gè)元素的指針入愧,然后將tuple對(duì)應(yīng)的值賦值給這個(gè)指針對(duì)應(yīng)的值。
NSString *str1 = @"1";
NSString *str2 = @"2";
NSString *str3 = @"3";
NSArray *array = @[[NSValue valueWithPointer:&str1],
[NSValue valueWithPointer:&str2],
[NSValue valueWithPointer:&str3]];
NSLog(@"str1 = %@, str2 = %@, str3 = %@", str1, str2, str3);
RACTupleUnpackingTrampoline *tupleTramp = RACTupleUnpackingTrampoline.trampoline;
RACTuple *tuple = RACTuplePack(@"a", @"b", @"c");
[tupleTramp setObject:tuple forKeyedSubscript:array];
NSLog(@"str1 = %@, str2 = %@, str3 = %@", str1, str2, str3);
看看輸出的結(jié)果:
ReactiveCocoaTest[78079:10162531] str1 = 3, str2 = 4
ReactiveCocoaTest[78079:10162531] st1 = 3, st2 = 4
其實(shí)這樣的方法是沒(méi)有什么實(shí)際用處的嗤谚,也不會(huì)有人這么去寫(xiě)棺蛛。但是定義了這個(gè)類(lèi),就肯定有它的實(shí)際意義巩步。接下來(lái)我們看看RACTuple
的兩個(gè)宏定義旁赊。
RACTuplePack、RACTupleUnpack
- RACTuplePack
將數(shù)據(jù)包裝成一個(gè)RACTuple類(lèi)型椅野。至少有一個(gè)數(shù)據(jù)终畅。
/// Creates a new tuple with the given values. At least one value must be given.
/// Values can be nil.
#define RACTuplePack(...) \
RACTuplePack_(__VA_ARGS__)
#define RACTuplePack_(...) \
([RACTuple tupleWithObjectsFromArray:@[ metamacro_foreach(RACTuplePack_object_or_ractuplenil,, __VA_ARGS__) ]])
#define RACTuplePack_object_or_ractuplenil(INDEX, ARG) \
(ARG) ?: RACTupleNil.tupleNil,
首先這個(gè)宏定義中使用了[RACTuple tupleWithObjectsFromArray:]
這個(gè)函數(shù)來(lái)創(chuàng)建一個(gè)新的tuple。
RACTuple *tuple = RACTuplePack(@1, @2);
- RACTupleUnpack
/// RACTupleUnpack(NSString *string, NSNumber *num) = [RACTuple tupleWithObjects:@"foo", @5, nil];
/// NSLog(@"string: %@", string);
/// NSLog(@"num: %@", num);
///
/// /* The above is equivalent to: */
/// RACTuple *t = [RACTuple tupleWithObjects:@"foo", @5, nil];
/// NSString *string = t[0];
/// NSNumber *num = t[1];
/// NSLog(@"string: %@", string);
/// NSLog(@"num: %@", num);
#define RACTupleUnpack(...) \
RACTupleUnpack_(__VA_ARGS__)
在源文件中已經(jīng)給出了例子了竟闪,就不在舉例子了离福。我們仔細(xì)看看這個(gè)宏是怎么搞的
#define RACTupleUnpack_(...) \
metamacro_foreach(RACTupleUnpack_decl,, __VA_ARGS__) \
\
int RACTupleUnpack_state = 0; \
\
RACTupleUnpack_after: \
; \
metamacro_foreach(RACTupleUnpack_assign,, __VA_ARGS__) \
if (RACTupleUnpack_state != 0) RACTupleUnpack_state = 2; \
\
while (RACTupleUnpack_state != 2) \
if (RACTupleUnpack_state == 1) { \
goto RACTupleUnpack_after; \
} else \
for (; RACTupleUnpack_state != 1; RACTupleUnpack_state = 1) \
[RACTupleUnpackingTrampoline trampoline][ @[ metamacro_foreach(RACTupleUnpack_value,, __VA_ARGS__) ] ]
一大堆的代碼,如果一級(jí)一級(jí)的去看這個(gè)宏中的內(nèi)容炼蛤,會(huì)發(fā)現(xiàn)很復(fù)雜妖爷,但是我們看到了最后一行代碼,上面提到的類(lèi)在這里是有用處的理朋。
我們寫(xiě)個(gè)例子絮识,對(duì)這個(gè)宏進(jìn)行編譯一下
RACTupleUnpack(NSString *st1, NSString *st2) = [RACTuple tupleWithObjects:@"3", @"4", nil];
這個(gè)代碼的形式跟Swift的元組很像绿聘。
let (intA, intB) = (1, 2)
接下來(lái),我們對(duì)這段代碼進(jìn)行編譯:
__attribute__((objc_ownership(strong))) id RACTupleUnpack79_var0;
__attribute__((objc_ownership(strong))) id RACTupleUnpack79_var1;
int RACTupleUnpack_state79 = 0; RACTupleUnpack_after79: ;
__attribute__((objc_ownership(strong))) NSString *st1 = RACTupleUnpack79_var0;
__attribute__((objc_ownership(strong))) NSString *st2 = RACTupleUnpack79_var1;
if (RACTupleUnpack_state79 != 0)
RACTupleUnpack_state79 = 2;
while (RACTupleUnpack_state79 != 2)
if (RACTupleUnpack_state79 == 1) {
goto RACTupleUnpack_after79;
}
else
for (; RACTupleUnpack_state79 != 1; RACTupleUnpack_state79 = 1)
[RACTupleUnpackingTrampoline trampoline][ @[ [NSValue valueWithPointer:&RACTupleUnpack79_var0], [NSValue valueWithPointer:&RACTupleUnpack79_var1], ] ] = [RACTuple tupleWithObjects:@"3", @"4", ((void *)0)];
看到這一坨代碼次舌,到最后是不是感覺(jué)瞬間懂了熄攘。
通過(guò)[NSValue valueWithPointer:]
獲取指針?lè)旁跀?shù)組中。然后通過(guò)賦值的方式將數(shù)據(jù)分別傳給兩個(gè)指針彼念。
總結(jié):聊一聊Push-Driven挪圾、Pull-Driven
RACSignal和RACSequence都是繼承自RACStream。信號(hào)是Push-Driven的信號(hào)流国拇,序列是Pull-Driven的序列流洛史。
Push-driven means that values for the signal are not defined at the moment of signal creation and may become available at a later time (for example, as a result from network request, or any user input).
Push-driven : 在創(chuàng)建信號(hào)的時(shí)候,信號(hào)不會(huì)被立即賦值酱吝,之后才會(huì)被賦值(舉個(gè)栗子:網(wǎng)絡(luò)請(qǐng)求回來(lái)的結(jié)果或者是任意的用戶(hù)輸入的結(jié)果)。
Pull-driven means that values in the sequence are defined at the moment of signal creation and we can query values from the stream one-by-one.
Pull-driven : 在創(chuàng)建信號(hào)的同時(shí)序列中的值就會(huì)被確定下來(lái)土思,我們可以從流中一個(gè)個(gè)地查詢(xún)值务热。
RACSequence <=> RACSignal
RACSequence也可以轉(zhuǎn)換為RACSignal,轉(zhuǎn)換時(shí)需要把sequence里面的值放到一個(gè)RACScgeduler中己儒,按照順序把值全部push到新創(chuàng)建的RACSignal中崎岂。
RACSignal也可以轉(zhuǎn)換為RACSequence,轉(zhuǎn)換時(shí)需要收集所有發(fā)送到這個(gè)signal的值闪湾,直到complete完成冲甘。會(huì)用到[signal toArray]
這個(gè)方法,這個(gè)方法會(huì)阻塞當(dāng)前線(xiàn)程途样。
RACSequence:是Pull-Driven江醇。除了特別聲明(eager)都是懶加載。在創(chuàng)建時(shí)何暇,序列中的值就已經(jīng)確定陶夜。當(dāng)被訂閱時(shí)會(huì)直接返還給訂閱者。當(dāng)轉(zhuǎn)換為signal后裆站,會(huì)將數(shù)據(jù)按照順序全部push到信號(hào)条辟。
RACSignal:是Push-Driven。signal是有訂閱者的存在才有意義(一般的信號(hào)都是冷信號(hào)宏胯,被創(chuàng)建之后羽嫡,只有訂閱才會(huì)被激活)。signal發(fā)送的不僅僅是數(shù)據(jù)肩袍,還包括狀態(tài)(error杭棵,complete)。
下一節(jié)來(lái)重點(diǎn)說(shuō)說(shuō)bind:
等了牛。
參考鏈接:
Reactive Cocoa Tutorial [3] = RACSignal的巧克力工廠
謝謝大家颜屠,有寫(xiě)的不對(duì)的地方辰妙,歡迎指正,共同進(jìn)步甫窟。