RAC總結(jié)

ReactiveCocoa 是一個函數(shù)響應(yīng)式編程(FRP)的框架。

一、導(dǎo)入框架

如果是純OC項目,使用pod 'ReactiveObjC'導(dǎo)入OC版RAC即可。
如果是純Swift項目,按照下圖方式導(dǎo)入:

RAC-Swift.png

由于Swift要使用動態(tài)庫,所以加入use_frameworks!這行代碼。

二、信號類RACSignal

RACSiganl:信號類,一般表示將來有數(shù)據(jù)傳遞,只要有數(shù)據(jù)改變,信號內(nèi)部接收到數(shù)據(jù),就會馬上發(fā)出數(shù)據(jù)。

信號類(RACSiganl),只是表示當數(shù)據(jù)改變時,信號內(nèi)部會發(fā)出數(shù)據(jù),它本身不具備發(fā)送信號的能力,而是交給內(nèi)部一個訂閱者去發(fā)出。

  • 獲得一個信號的方式

1.單元信號

RACSignal *signal1 = [RACSignal return:@"value"];
RACSignal *signal2 = [RACSignal error:error];
RACSignal *signal3 = [RACSignal empty];
RACSignal *signal4 = [RACSignal never];

2.動態(tài)信號

RACSignal *signal5 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    [subscriber sendNext:@1];
    [subscriber sendNext:@2];
    [subscriber sendError:error];
    [subscriber sendCompleted];
    return [RACDisposable disposableWithBlock:^{
        
    }];
}];

3.Cocoa橋接

// 監(jiān)測對象的該方法是否調(diào)用
RACSignal *signal6 = [view rac_signalForSelector:@selector(setFrame:)];
// 監(jiān)測對象是否觸發(fā)了該事件
RACSignal *signal7 = [button rac_signalForControlEvents:UIControlEventTouchUpInside];
// 對象將要釋放時發(fā)出該信號
RACSignal *signal8 = [view rac_willDeallocSignal];
// 監(jiān)測對象的某個屬性是否變化,一旦變化就會發(fā)出信號
RACSignal *signal9 = RACObserve(view, backgroundColor);

4.信號變換

// 對信號每發(fā)出一個值進行處理,再返給信號
RACSignal *signal10 = [signal map:^id(NSString *value) {
    return [value substringFromIndex:1];
}];

5.序列轉(zhuǎn)換

// 集合也能被轉(zhuǎn)換為信號進行操作
RACSignal *signal11 = collection.rac_sequence.signal;
  • 訂閱一個信號的方式

1.訂閱方法

// 直接調(diào)用 subscribeNext 方法進行訂閱
[signal11 subscribeNext:^(id x) {
    NSLog(@"value %@", x);
} error:^(NSError *error) {
    NSLog(@"error %@", error);
} completed:^{
    NSLog(@"finished");
}];

2.綁定

// 將對象屬性與信號值綁定
RAC(self.loginButton, enabled) = signal;

3.Cocoa橋接

// 兩個信號都sendNext后才會調(diào)用selector
[view rac_liftSelector:@selector(data1:data2:) withSignalsFromArray:@[signal6, signal7]];
  • 值操作

1.Map

// 把信號A中的值取出乘2后返回給信號B
RACSignal *signalB = [signalA map:^id(NSNumber *value) {
    return @(value.integerValue * 2);
}];

2.MapReplace

// 把信號A中的值都替換為8
RACSignal *signalC = [signalA mapReplace:@8];
// 效果等同于
RACSignal *signalB = [signalA map:^id(id value) {
    return @8;
}];

3.ReduceEach

// reduceEach與map類似,不過只有signalA包含的值是元組類型時才能用它
// 信號A中包含了N個元組,每個元組內(nèi)有兩個元素,使用 reduceEach 將每個元組中的兩個數(shù)字取出并相加,返回給信號B
RACSignal *signalB = [signalA reduceEach:^id(NSNumber *first, NSNumber *second) {
    return @(first.integerValue + second.integerValue);
}];
  • 數(shù)量操作

1.Filter

// 過濾掉長度大于2的值
RACSignal *signalB = [signalA filter:^BOOL(NSString *value) {
    return value.length > 2;
}];

2.Ignore

// 忽略為1的值
RACSignal *signalC = [signalA ignore:@1];
// 效果等同于
RACSignal *signalB = [signalA filter:^BOOL(id value) {
    return ![@1 isEqual:value];
}];
// 忽略所有的值
- (RACSignal *)ignoreValues;
// 與上次的值不同時才發(fā)信號
- (RACSignal *)distinctUntilChanged;
// 只會收到一次信號
RACSubject *subject = [RACSubject subject];
[[subject distinctUntilChanged] subscribeNext:^(id x) {
    NSLog(@"%@", x);
}];
[subject sendNext:@"HMJ"];
[subject sendNext:@"HMJ"];
[subject sendNext:@"HMJ"];

3.Take

// 取A的前兩個值
RACSignal *signalB = [signalA take:2];

4.Take&Skip其它

// 取最后N次的值,會在信號完成后發(fā)送
- (RACSignal *)takeLast:(NSUInteger)count;
// 一直取值,直到block返回YES,如下例子
- (RACSignal *)takeUntilBlock:(BOOL (^)(id x))predicate;
// block返回YES才取值
- (RACSignal *)takeWhileBlock:(BOOL (^)(id x))predicate;
// 原理同上
- (RACSignal *)skipUntilBlock:(BOOL (^)(id x))predicate;
// 原理同上
- (RACSignal *)skipWhileBlock:(BOOL (^)(id x))predicate;

[[self.inputTextField.rac_textSignal takeUntilBlock:^BOOL(NSString *value) {
    return [value isEqualToString:@"stop"];
}] subscribeNext:^(NSString *value) {
    NSLog(@"current value is not `stop`: %@", value);
}];

5.StartWith

// 信號B以 Start 開頭
RACSignal *signalB = [signalA startWith:@"Start"];

6.Repeat

// 不停地重復(fù)信號A
RACSignal *signalB = [signalA repeat];

7.Retry

// 如果返回錯誤則重新訂閱,最多2次
RACSignal *signalB = [signalA retry:2];

8.副作用操作

RACSignal *signalB = [signalA map:^id(id value) {
    // do some thing
    return value;
}];
RACSignal *signalC = [signalA doNext:^(id x) {
    // do some thing
}];

- (RACSignal *)doError:(void (^)(NSError *error))block;
- (RACSignal *)doCompleted:(void (^)(void))block;
- (RACSignal *)initially:(void (^)(void))block;
- (RACSignal *)finally:(void (^)(void))block;

9.Collect

// 把信號A的值收集為數(shù)組
RACSignal *signalB = [signalA collect];

10.Aggregate

// 匯聚,前一次的運算結(jié)果為running,下次的信號值為next
// 如果signalA的值依次為1、2、3、4、5,匯聚之后就是25
RACSignal *signalB = [signalA aggregateWithStart:@0 reduce:^id(NSNumber *running, NSNumber *next) {
    return @(running.integerValue + next.integerValue);
}];

11.Scan

// 使用Aggregate時,如果A不停止,B就永遠不會返回值
// 而使用Scan就不會有這個弊端,每次運算結(jié)果都會返回
RACSignal *signalB = [signalA scanWithStart:@0 reduce:^id(NSNumber *running, NSNumber *next) {
    return @(running.integerValue + next.integerValue);
}];
  • 時間操作

1.Scheduler

// 比較準確的時間操作,可代替NSTimer
- (RACSignal *)interval:(NSTimeInterval)interval
            onScheduler:(RACScheduler *)scheduler;
- (RACSignal *)interval:(NSTimeInterval)interval
            onScheduler:(RACScheduler *)scheduler
             withLeeway:(NSTimeInterval)leeway;

2.Delay

// 準確度比 Scheduler 低
RACSignal *signalB = [signalA delay:1];

3.Throttle

// 閥門時間,設(shè)置時間內(nèi)如果沒有新的信號才返回值,否則就重新開始計時
RACSignal *signalB = [signalA throttle:1];
  • 組合操作

1.Concat

// 按信號順序合并信號,第一個信號如果不停止,第二個信號就永遠無法開始接收
// 可用于多網(wǎng)絡(luò)請求時,返回數(shù)據(jù)需要按順序拼接的場景
RACSignal *signalC = [signalA concat:signalB];

2.Merge

// 將多個信號合并成一個信號,按照發(fā)送值的順序來合并,屬于無序合并
RACSignal *signalC = [signalA merge:signalB];
RACSignal *signalC = [RACSignal merge:@[signalA, signalB]];
RACSignal *signalC = [RACSignal merge:RACTuplePack(signalA, signalB)];

3.Zip

// 合并兩個信號的最新值為元組,只有兩個信號都有新值時才會合并
RACSignal *signalC = [signalA zipWith:signalB];
RACSignal *signalC = [RACSignal zip:@[signalA, signalB]];
RACSignal *signalC = [RACSignal zip:RACTuplePack(signalA, signalB)];

4.CombineLatest

// 與zip相似,都是合并兩個信號的值為元組,不過combineLatest只需一個信號值改變就能合并
RACSignal *signalC = [signalA combineLatestWith:signalB];
RACSignal *signalC = [RACSignal combineLatest:@[signalA, signalB]];

5.Sample

// B觸發(fā)時才去獲取A的實時值
RACSignal *signalC = [signalA sample:signalB];

6.TakeUntil

// 獲取信號A,B開始就停止
RACSignal *signalC = [signalA takeUntil:signalB];

7.TakeUntilReplacement

// 獲取信號A,B開始就停止獲取A,轉(zhuǎn)而獲取B,直到B停止
RACSignal *signalC = [signalA takeUntilReplacement:signalB];
  • 訂閱高階信號

如果信號中包含了信號,它就是高階信號。
下面代碼中信號highOrderSignal中包含了信號,所以它是高階信號。

RACSignal *signal = @[@1, @2, @3].rac_sequence.signal;
RACSignal *highOrderSignal = [signal map:^id(id value) {
    return [RACSignal return:value];
}];
[highOrderSignal subscribeNext:^(RACSignal *aSignal) {
    [aSignal subscribeNext:^(id x) {
        NSLog(@"%@", x);
    }];
}];

將信號的值轉(zhuǎn)變成信號叫做信號的升階,將信號中的信號轉(zhuǎn)變成值叫做信號的降階。

  • 降階操作

1.SwitchToLatest

switchToLatest能夠?qū)Ω唠A信號進行降階,它總是訂閱最新的信號。

RACSignal *signalB = [signalA switchToLatest];

// 如下示例,訂閱后最終收到的值為@"1"、@"11"、@"2"。
// 沒有收到@"111"是因為它被發(fā)送時,sub1已經(jīng)不是最新信號了
// 要注意,最新信號發(fā)生error會中斷l(xiāng)atest信號
RACSubject *signalOfSignals = [RACSubject subject];
RACSubject *sub1 = [RACSubject subject];
RACSubject *sub2 = [RACSubject subject];
RACSignal *latest = [signalOfSignals switchToLatest];
[latest subscribeNext:^(id x) {
    NSLog(@"***** %@",x);
}];
[signalOfSignals sendNext:sub1];
[sub1 sendNext:@"1"];
[sub1 sendNext:@"11"];
[signalOfSignals sendNext:sub2];
[sub2 sendNext:@"2"];
[sub1 sendNext:@"111"];

2.If/then/else

如果 signalA 的值為 true 取 signalTrue 的值,否則取 signalFalse 的值。

RACSignal *signalB = [RACSignal if:signalA
                              then:signalTrue
                              else:signalFalse];

3.Flatten
flatten進行的降階與switchToLatest不同,它能夠獲取所有的值。

RACSignal *signalB = [signalA flatten];

方法- (RACSignal *)flatten:(NSUInteger)maxConcurrent中,maxConcurrent表示最大并發(fā)數(shù)。比如 flatten:2 表示當前最多只能對信號中的2個信號進行降階操作,如果有新的信號產(chǎn)生,先進行等待,等到前面有信號結(jié)束之后才加入。

所以 Flatten 類似于 Merge,F(xiàn)latten:1 類似于 Concat。

4.FlattenMap
高階信號可以用flattenMap進行映射,在block中再對映射出的低階信號進行映射,最后返回的信號就是降維后的信號。

  • 冷信號與熱信號

熱信號:
熱信號是主動的,即使你沒有訂閱事件,它仍然會不斷推送。
熱信號可以有多個訂閱者,是一對多,信號可以與訂閱者共享信息。

冷信號:
冷信號是被動的,只有當你訂閱的時候,它才會發(fā)送消息。
冷信號只能一對一,當有新的訂閱者時,消息會重新完整發(fā)送。

在 RAC 中,RACSubject及其子類是熱信號,RACSignalRACSubject以外的信號是冷信號。

1.熱信號

RACSubject需要先訂閱再發(fā)送:

RACSubject *subject = [RACSubject subject];
[subject subscribeNext:^(id x) {
    //
} error:^(NSError *error) {
    //
} completed:^{
    //
}];

[subject sendNext:@1];
[subject sendNext:@2];
[subject sendNext:@3];

[subject sendCompleted];

RACReplaySubject可先發(fā)送再訂閱:

RACReplaySubject *subject = [RACReplaySubject replaySubjectWithCapacity:1];

[subject sendNext:@1];
[subject sendNext:@2];
[subject sendCompleted];

[subject subscribeNext:^(id x) {
    //
}];

RACReplaySubject是具備快速回播的信號,同時它還能控制“歷史值”的數(shù)量。

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

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