ReactiveCocoa2.5基礎(chǔ)知識(shí)
[toc]
ReactiveCocoa
是一個(gè) iOS 中的函數(shù)式響應(yīng)式編程框架,本文中簡(jiǎn)稱RAC
崭篡。在開(kāi)發(fā) GitHub for Mac
過(guò)程中的一個(gè)副產(chǎn)品,它提供了一系列用來(lái)組合和轉(zhuǎn)換值流的 API 。本文只是記錄自己學(xué)到ReactiveCocoa的筆記觉壶。
ReactiveCocoa核心組件
信號(hào)源
RACStream 有兩個(gè)派生類
RACSequence :基于空間的數(shù)據(jù)流(在RAC中很少使用)
創(chuàng)建
RACSequence *sequence1 = [RACSequence return:@1];
NSLog(@"%@",sequence1.head);
// 輸出:1
RACSequence *sequence2 = [RACSequence sequenceWithHeadBlock:^id{
return @2;
} tailBlock:^RACSequence *{
return sequence1;
}];
NSLog(@"%@ %@",sequence2.head,sequence2.tail.head);
// 輸出:2 1
RACSequence *sequence3 = @[@4,@5,@6,@7].rac_sequence;
for (id value in concatSquence) {
NSLog(@"%@",value);
}
// 輸出:4
// 輸出:5
// 輸出:6
// 輸出:7
變換
RACSequence *mapSrquence = [sequence1 map:^id(NSNumber *value) {
return @(value.integerValue *3); // 1 * 3
}];
NSLog(@"%@",mapSrquence.head);
// 輸出:3
// concat:合并吮廉,目的:有兩部分?jǐn)?shù)據(jù),想讓上部分先執(zhí)行入挣,完了之后再讓下部分執(zhí)行(都可獲取值)
RACSequence *concatSquence = [sequence2 concat:mapSrquence];
for (id value in concatSquence) {
NSLog(@"%@",value);
}
// 輸出:2
// 輸出:1
// 輸出:3
concatSquence: 2 1 3
sequence3 : 4 5 6 7
RACSequence *zipSquence = [RACSequence zip:@[concatSquence,sequence3]];
遍歷
for (RACTuple *tuple in zipSquence) {
NSLog(@"%@---%@",tuple.first,tuple.second);
}
// 輸出:2---4
// 輸出:1---5
// 輸出:3---6
RACSignal :基于時(shí)間的數(shù)據(jù)流
1.使用基礎(chǔ)
- 單元信號(hào)
RACSignal *signal1 = [RACSignal return:@1];
RACSignal *signal2 = [RACSignal error:errorObject];
RACSignal *signal3 = [RACSignal empty];
RACSignal *signal4 = [RACSignal never];
- 動(dòng)態(tài)信號(hào)
RACSignal *signal5 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@1];
[subscriber sendNext:@2];
// 如果出現(xiàn)錯(cuò)誤,sendCompleted就會(huì)失效
// [subscriber sendError:errorObject];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
}];
}];
- Cocoa橋接
// 監(jiān)聽(tīng)
RACSignal *signal6 = [view rac_signalForSelector:@selector(setFrame)];
// 事件點(diǎn)擊
RACSignal *signal7 = [button rac_signalForControlEvents:UIControlEventTouchUpInside];
// dealloc
RACSignal *signal8 = [view rac_willDeallocSignal];
// KVO
RACSignal *signal9 = RACObserve(view, backgroundColor);
- 信號(hào)變換
RACSignal *signal10 = [signal1 map:^id(NSString *value) {
return [value substringFromIndex:1];
}];
- 序列轉(zhuǎn)換
RACSequence *sequence = [RACSequence return:@1];
RACSignal *signal11 = sequence.signal;
2.訂閱一個(gè)信號(hào)的方式
2.1 ) 訂閱方法
// 有三種參數(shù)方法
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock;
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock completed:(void (^)(void))completedBlock;
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock;
example:
[signal11 subscribeNext:^(id x) {
NSLog(@"%@",x);
} error:^(NSError *error) {
NSLog(@"error");
} completed:^{
NSLog(@"finished");
}];
2.2 ) 綁定
RAC(view,backgroundColor) = signal10;
2.3 ) Cocoa橋接
// rac_liftSelector: 相當(dāng)于多線程組;所有信號(hào)請(qǐng)求完成之后再執(zhí)行方法归粉,只需把三個(gè)結(jié)果傳出去即可
[view rac_liftSelector:@selector(updateUIWithR1:r2:) withSignals:signal1,signal3, nil];
[view rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalsFromArray:@[signal1,signal1]];
[view rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalOfArguments:signal1];
3.單個(gè)信號(hào)的變換
3.1 ) 值操作
-
Map:映射,目的:把原始值value映射成一個(gè)新值
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@1];
[subscriber sendNext:@2];
[subscriber sendNext:@3];
[subscriber sendCompleted];
return nil;
}];
RACSignal *signalB = [signalA map:^id(NSNumber *value) {
return @(value.integerValue * 3);
}];
[signalB subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 輸出:3
// 輸出:6
// 輸出:9
- mapReplace:Map簡(jiǎn)化操作漏峰,只返回一個(gè)相同的信號(hào)
RACSignal *signalC = [signalA mapReplace:@8];
[signalC subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 輸出:8
// 輸出:8
// 輸出:8
-
reduceEach:Map的變體糠悼,當(dāng) signalA 的值事件包裹的數(shù)據(jù)是 RACTuple 類型時(shí),才可以使用該操作
RACSignal *signalD = [signalA reduceEach:^id(NSNumber *num1 , NSNumber *num2){
return @([num1 intValue] + [num2 intValue]);
}];
- not:需要傳入的值都是NSNumber類型的,不是NSNumber類型會(huì)報(bào)錯(cuò)
- not操作會(huì)把每個(gè)NSNumber按照BOOL的規(guī)則浅乔,取非倔喂,當(dāng)成新信號(hào)的值。
- (RACSignal *)not {
return [[self map:^(NSNumber *value) {
NSCAssert([value isKindOfClass:NSNumber.class], @"-not must only be used on a signal of NSNumbers. Instead, got: %@", value);
return @(!value.boolValue);
}] setNameWithFormat:@"[%@] -not", self.name];
}
- and操作需要原信號(hào)的每個(gè)信號(hào)都是元組RACTuple類型的靖苇,因?yàn)橹挥羞@樣席噩,RACTuple類型里面的每個(gè)元素的值才能進(jìn)行&運(yùn)算。
- and操作里面有3處斷言顾复。第一處班挖,判斷入?yún)⑹遣皇窃MRACTuple類型的。第二處芯砸,判斷RACTuple類型里面至少包含一個(gè)NSNumber萧芙。第三處,判斷RACTuple里面是否都是NSNumber類型假丧,有一個(gè)不符合双揪,都會(huì)報(bào)錯(cuò)。
- (RACSignal *)and {
return [[self map:^(RACTuple *tuple) {
NSCAssert([tuple isKindOfClass:RACTuple.class], @"-and must only be used on a signal of RACTuples of NSNumbers. Instead, received: %@", tuple);
NSCAssert(tuple.count > 0, @"-and must only be used on a signal of RACTuples of NSNumbers, with at least 1 value in the tuple");
return @([tuple.rac_sequence all:^(NSNumber *number) {
NSCAssert([number isKindOfClass:NSNumber.class], @"-and must only be used on a signal of RACTuples of NSNumbers. Instead, tuple contains a non-NSNumber value: %@", tuple);
return number.boolValue;
}]);
}] setNameWithFormat:@"[%@] -and", self.name];
}
- or操作的實(shí)現(xiàn)和and操作的實(shí)現(xiàn)大體類似包帚。3處斷言的作用和and操作完全一致渔期,這里就不再贅述了。or操作的重點(diǎn)在any函數(shù)的實(shí)現(xiàn)上。or操作的入?yún)⒁脖仨毷荝ACTuple類型的疯趟。
- (RACSignal *)or {
return [[self map:^(RACTuple *tuple) {
NSCAssert([tuple isKindOfClass:RACTuple.class], @"-or must only be used on a signal of RACTuples of NSNumbers. Instead, received: %@", tuple);
NSCAssert(tuple.count > 0, @"-or must only be used on a signal of RACTuples of NSNumbers, with at least 1 value in the tuple");
return @([tuple.rac_sequence any:^(NSNumber *number) {
NSCAssert([number isKindOfClass:NSNumber.class], @"-or must only be used on a signal of RACTuples of NSNumbers. Instead, tuple contains a non-NSNumber value: %@", tuple);
return number.boolValue;
}]);
}] setNameWithFormat:@"[%@] -or", self.name];
}
- 這里也有2個(gè)斷言拘哨,第一個(gè)是確保傳入的參數(shù)是RACTuple類型,第二個(gè)斷言是確保元組RACTuple里面的元素各種至少是2個(gè)信峻。因?yàn)橄旅嫒?shù)是直接從1號(hào)位開(kāi)始取的倦青。
- reduceApply做的事情和reduceEach基本是等效的,只不過(guò)變換規(guī)則的block閉包一個(gè)是外部傳進(jìn)去的盹舞,一個(gè)是直接打包在每個(gè)信號(hào)元組RACTuple中第0位中产镐。
- (RACSignal *)reduceApply {
return [[self map:^(RACTuple *tuple) {
NSCAssert([tuple isKindOfClass:RACTuple.class], @"-reduceApply must only be used on a signal of RACTuples. Instead, received: %@", tuple);
NSCAssert(tuple.count > 1, @"-reduceApply must only be used on a signal of RACTuples, with at least a block in tuple[0] and its first argument in tuple[1]");
// We can't use -array, because we need to preserve RACTupleNil
NSMutableArray *tupleArray = [NSMutableArray arrayWithCapacity:tuple.count];
for (id val in tuple) {
[tupleArray addObject:val];
}
RACTuple *arguments = [RACTuple tupleWithObjectsFromArray:[tupleArray subarrayWithRange:NSMakeRange(1, tupleArray.count - 1)]];
return [RACBlockTrampoline invokeBlock:tuple[0] withArguments:arguments];
}] setNameWithFormat:@"[%@] -reduceApply", self.name];
}
// 舉個(gè)例子
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber){
id block = ^id(NSNumber *first,NSNumber *second,NSNumber *third) {
return @(first.integerValue + second.integerValue * third.integerValue);
};
[subscriber sendNext:RACTuplePack(block,@1 , @3 , @5)];
[subscriber sendNext:RACTuplePack((id)(^id(NSNumber *x){return @(x.intValue * 10);}),@10,@20,@30)];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
}];
}];
RACSignal *signalB = [signalA reduceApply];
[signalB subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 輸出:16 = 1 + 3*5
// 輸出:100 = 10 * 10
- materialize把信號(hào)包裝成RACEvent類型。
- (RACSignal *)materialize {
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
return [self subscribeNext:^(id x) {
[subscriber sendNext:[RACEvent eventWithValue:x]];
} error:^(NSError *error) {
[subscriber sendNext:[RACEvent eventWithError:error]];
[subscriber sendCompleted];
} completed:^{
[subscriber sendNext:RACEvent.completedEvent];
[subscriber sendCompleted];
}];
}] setNameWithFormat:@"[%@] -materialize", self.name];
}
- dematerialize操作是materialize的逆向操作踢步。它會(huì)把包裝成RACEvent信號(hào)重新還原為正常的值信號(hào)癣亚。
- (RACSignal *)dematerialize {
return [[self bind:^{
return ^(RACEvent *event, BOOL *stop) {
switch (event.eventType) {
case RACEventTypeCompleted:
*stop = YES;
return [RACSignal empty];
case RACEventTypeError:
*stop = YES;
return [RACSignal error:event.error];
case RACEventTypeNext:
return [RACSignal return:event.value];
}
};
}] setNameWithFormat:@"[%@] -dematerialize", self.name];
}
3.2 ) 數(shù)量操作
- Filter:過(guò)濾
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"ab"];
[subscriber sendNext:@"hello"];
[subscriber sendNext:@"world"];
[subscriber sendNext:@"123"];
[subscriber sendCompleted];
return nil;
}];
RACSignal *signalC = [signalA filter:^BOOL(NSString *value) {
return value.length > 3;
}];
[signalC subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 輸出:hello
// 輸出:world
- Ignore:忽略
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"Ab"];
[subscriber sendNext:@"hello"];
[subscriber sendNext:@"world"];
[subscriber sendNext:@"123"];
[subscriber sendCompleted];
return nil;
}];
RACSignal *signalB = [signalA filter:^BOOL(NSString *value) {
return ![value isEqual:@"ab"];
}];
[signalB subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 輸出:hello
// 輸出:world
// 輸出:123
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"Ab"];
[subscriber sendNext:@"hello"];
[subscriber sendNext:@"world"];
[subscriber sendNext:@"123"];
[subscriber sendCompleted];
return nil;
}];
RACSignal *signalC = [signalA ignore:@"ab"];
[signalC subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 輸出:hello
// 輸出:world
// 輸出:123
ignoreValues:忽略所有的值
distinctUntilChanged:讀取所有的值
-
Take:取值的數(shù)量
-
Skip:跳過(guò)
-
StartWith:初始位置添加
-
Repeat:重復(fù)說(shuō)(一直不停止)
-
Retry:遇到錯(cuò)誤繼續(xù)嘗試
副作用
DoNext:副作用操作,做一些事情
- (RACSignal *)doError:(void (^)(NSError *error))block;
- (RACSignal *)doCompleted:(void (^)(void))block;
- (RACSignal *)initially:(void (^)(void))block;
- (RACSignal *)finally:(void (^)(void))block;
-
Collect收集源信號(hào)的所有sendNext事件获印,直到源信號(hào)發(fā)出sendComplete事件述雾,才將收集好的sendNext事件發(fā)出去
-
Aggregate折疊函數(shù),和Scan這兩個(gè)操作有些類似蓬豁,但明顯有區(qū)別绰咽,前者改變了事件流的事件數(shù)量,后者沒(méi)有
-
Scan每次計(jì)算結(jié)果都會(huì)給到一個(gè)加和
3.3 ) 時(shí)間操作
-
Delay延遲
-
Throttle:閥門(mén)
3.4 ) 組合操作
-
Concat:連接
-
Merge:匯合
RACSignal *signalC = [signalA merge:signalB];
RACSignal *signalD = [RACSignal merge:@[signalA,signalB]];
RACSignal *signalE = [RACSignal merge:RACTuplePack(signalA,signalB)];
-
zip:拉鏈地粪,目的:把兩個(gè)信號(hào)壓縮成一個(gè)信號(hào)取募,并且把兩個(gè)信號(hào)的內(nèi)容合并成一個(gè)元組
RACSignal *signalC = [signalA zipWith:signalB];
RACSignal *signalD = [RACSignal zip:@[signalA,signalB]];
RACSignal *signalE = [RACSignal zip:RACTuplePack(signalA,signalB)];
-
CombineLatest:和數(shù)學(xué)中的邏輯與(&&)的概念是一樣的择浊,只有兩個(gè)兩個(gè)信號(hào)同時(shí)發(fā)送了sendNext事件宁仔,才會(huì)觸發(fā)
RACSignal *signalC = [signalA combineLatestWith:signalB];
RACSignal *signalD = [RACSignal combineLatest:@[signalA,signalB]];
-
Sample:采樣
-
TakeUntil:當(dāng)下一個(gè)信號(hào)傳遞的時(shí)候停止訂閱信號(hào)
-
TakeUntilReplacement:當(dāng)下一個(gè)信號(hào)傳遞的時(shí)候停止訂閱信號(hào),并返回加和下個(gè)信號(hào)
兩者的區(qū)別
Pull-driver vs Push-driver
例子:看電影
Sequence:看本地下載的電影,突然想上廁所闯第,可以暫停质礼,上完廁所回來(lái)再看(觀看者決定)
Signal:在線看電影旺聚,不管你看不看電影一直都在播放(發(fā)送者決定)
Data vs Event
Sequence: 任何ID類型的數(shù)據(jù)(值)
Signal:基于信號(hào)(值,錯(cuò)誤眶蕉,終止砰粹,中斷)
訂閱者
RACSubscriber
訂閱過(guò)程
對(duì)于訂閱過(guò)程不了解可以查看這三篇博客
細(xì)說(shuō)ReactiveCocoa的冷信號(hào)與熱信號(hào)(一)
細(xì)說(shuō)ReactiveCocoa的冷信號(hào)與熱信號(hào)(二)
細(xì)說(shuō)ReactiveCocoa的冷信號(hào)與熱信號(hào)(三)
RACSignal *signal5 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@1];
[subscriber sendNext:@2];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"dispose");
}];
}];
RACDisposable *disposable = [signal5 subscribeNext:^(id x) {
NSLog(@"%@",x);
} error:^(NSError *error) {
NSLog(@"error");
} completed:^{
NSLog(@"finished");
}];
[disposable dispose];
調(diào)度器
RACScheduler
RACScheduler
在 ReactiveCocoa
中就是扮演著調(diào)度器的角色,本質(zhì)上造挽,它就是用 GCD
的串行隊(duì)列來(lái)實(shí)現(xiàn)的碱璃,并且支持取消操作。是的饭入,在 ReactiveCocoa
中嵌器,并沒(méi)有使用到 NSOperationQueue
和 NSRunloop
等技術(shù),RACScheduler
也只是對(duì) GCD
的簡(jiǎn)單封裝而已谐丢。
清潔工
RACDisposable
RACDisposable
在 ReactiveCocoa
中就充當(dāng)著清潔工的角色爽航,它封裝了取消和清理一次訂閱所必需的工作蚓让。它有一個(gè)核心的方法 -dispose
,調(diào)用這個(gè)方法就會(huì)執(zhí)行相應(yīng)的清理工作讥珍,這有點(diǎn)類似于 NSObject
的 -dealloc
方法