iOS最新面試題解答最全-2023-ReactiveCocoa

一、簡單介紹ReactiveCocoa

ReactiveCocoa(簡稱為RAC),是由Github開源的一個(gè)應(yīng)用于iOS和OS開發(fā)的新框架,Cocoa是蘋果整套框架的簡稱,因此很多蘋果框架喜歡以Cocoa結(jié)尾

二、ReactiveCocoa作用

在我們iOS開發(fā)過程中,當(dāng)某些事件響應(yīng)的時(shí)候,需要處理某些業(yè)務(wù)邏輯,這些事件都用不同的方式來處理。
比如按鈕的點(diǎn)擊使用action,ScrollView滾動(dòng)使用delegate,屬性值改變使用KVO等系統(tǒng)提供的方式。
其實(shí)這些事件,都可以通過RAC處理
ReactiveCocoa為事件提供了很多處理方法,而且利用RAC處理事件很方便,可以把要處理的事情,和監(jiān)聽的事情的代碼放在一起,這樣非常方便我們管理,就不需要跳到對(duì)應(yīng)的方法里。非常符合我們開發(fā)中高聚合,低耦合的思想。

三、編程思想

先簡單介紹下目前咱們已知的編程思想。
1 面向過程:處理事情以過程為核心,一步一步的實(shí)現(xiàn)。
2 面向?qū)ο螅喝f物皆對(duì)象
3 鏈?zhǔn)骄幊趟枷耄菏菍⒍鄠€(gè)操作(多行代碼)通過點(diǎn)號(hào)(.)鏈接在一起成為一句代碼,使代碼可讀性好。a(1).b(2).c(3)
鏈?zhǔn)骄幊烫攸c(diǎn):方法的返回值是block,block必須有返回值(本身對(duì)象),block參數(shù)(需要操作的值)
代表:masonry框架。
4 響應(yīng)式編程思想:不需要考慮調(diào)用順序,只需要知道考慮結(jié)果,類似于蝴蝶效應(yīng),產(chǎn)生一個(gè)事件,會(huì)影響很多東西,這些事件像流一樣的傳播出去,然后影響結(jié)果,借用面向?qū)ο蟮囊痪湓挘f物皆是流。
代表:KVO運(yùn)用。
5 函數(shù)式編程思想:是把操作盡量寫成一系列嵌套的函數(shù)或者方法調(diào)用。
函數(shù)式編程本質(zhì):就是往方法中傳入Block,方法中嵌套Block調(diào)用,把代碼聚合起來管理
函數(shù)式編程特點(diǎn):每個(gè)方法必須有返回值(本身對(duì)象),把函數(shù)或者Block當(dāng)做參數(shù),block參數(shù)(需要操作的值)block返回值(操作結(jié)果)
代表:ReactiveCocoa。

四、ReactiveCocoa編程思想

ReactiveCocoa結(jié)合了幾種編程風(fēng)格:

函數(shù)式編程(Functional Programming)

響應(yīng)式編程(Reactive Programming)

所以,你可能聽說過ReactiveCocoa被描述為函數(shù)響應(yīng)式編程(FRP)框架。

以后使用RAC解決問題,就不需要考慮調(diào)用順序,直接考慮結(jié)果,把每一次操作都寫成一系列嵌套的方法中,使代碼高聚合,方便管理。

五、ReactiveCocoa常見類

RACSiganl:信號(hào)類,一般表示將來有數(shù)據(jù)傳遞,只要有數(shù)據(jù)改變,信號(hào)內(nèi)部接收到數(shù)據(jù),就會(huì)馬上發(fā)出數(shù)據(jù)。
信號(hào)類(RACSiganl),只是表示當(dāng)數(shù)據(jù)改變時(shí),信號(hào)內(nèi)部會(huì)發(fā)出數(shù)據(jù),它本身不具備發(fā)送信號(hào)的能力,而是交給內(nèi)部一個(gè)訂閱者去發(fā)出。
默認(rèn)一個(gè)信號(hào)都是冷信號(hào),也就是值改變了,也不會(huì)觸發(fā),只有訂閱了這個(gè)信號(hào),這個(gè)信號(hào)才會(huì)變?yōu)闊嵝盘?hào),值改變了才會(huì)觸發(fā)。
如何訂閱信號(hào):調(diào)用信號(hào)RACSignal的subscribeNext就能訂閱。
RACCommand:RAC中用于處理事件的類,可以把事件如何處理,事件中的數(shù)據(jù)如何傳遞,包裝到這個(gè)類中,他可以很方便的監(jiān)控事件的執(zhí)行過程。
使用場(chǎng)景:監(jiān)聽按鈕點(diǎn)擊,網(wǎng)絡(luò)請(qǐng)求
RACCommand簡單使用
```

// 一、RACCommand使用步驟:
// 1.創(chuàng)建命令 initWithSignalBlock:(RACSignal * (^)(id input))signalBlock
// 2.在signalBlock中,創(chuàng)建RACSignal,并且作為signalBlock的返回值
// 3.執(zhí)行命令 - (RACSignal *)execute:(id)input

// 二、RACCommand使用注意:
// 1.signalBlock必須要返回一個(gè)信號(hào),不能傳nil.
// 2.如果不想要傳遞信號(hào),直接創(chuàng)建空的信號(hào)[RACSignal empty];
// 3.RACCommand中信號(hào)如果數(shù)據(jù)傳遞完,必須調(diào)用[subscriber sendCompleted],這時(shí)命令才會(huì)執(zhí)行完畢,否則永遠(yuǎn)處于執(zhí)行中。

// 三、RACCommand設(shè)計(jì)思想:內(nèi)部signalBlock為什么要返回一個(gè)信號(hào),這個(gè)信號(hào)有什么用。
// 1.在RAC開發(fā)中,通常會(huì)把網(wǎng)絡(luò)請(qǐng)求封裝到RACCommand,直接執(zhí)行某個(gè)RACCommand就能發(fā)送請(qǐng)求。
// 2.當(dāng)RACCommand內(nèi)部請(qǐng)求到數(shù)據(jù)的時(shí)候,需要把請(qǐng)求的數(shù)據(jù)傳遞給外界,這時(shí)候就需要通過signalBlock返回的信號(hào)傳遞了。

// 四、如何拿到RACCommand中返回信號(hào)發(fā)出的數(shù)據(jù)。
// 1.RACCommand有個(gè)執(zhí)行信號(hào)源executionSignals,這個(gè)是signal of signals(信號(hào)的信號(hào)),意思是信號(hào)發(fā)出的數(shù)據(jù)是信號(hào),不是普通的類型。
// 2.訂閱executionSignals就能拿到RACCommand中返回的信號(hào),然后訂閱signalBlock返回的信號(hào),就能獲取發(fā)出的值。

// 五、監(jiān)聽當(dāng)前命令是否正在執(zhí)行executing

// 六、使用場(chǎng)景,監(jiān)聽按鈕點(diǎn)擊,網(wǎng)絡(luò)請(qǐng)求


// 1.創(chuàng)建命令
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {


    NSLog(@"執(zhí)行命令");

    // 創(chuàng)建空信號(hào),必須返回信號(hào)
    //        return [RACSignal empty];

    // 2.創(chuàng)建信號(hào),用來傳遞數(shù)據(jù)
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

        [subscriber sendNext:@"請(qǐng)求數(shù)據(jù)"];

        // 注意:數(shù)據(jù)傳遞完,最好調(diào)用sendCompleted,這時(shí)命令才執(zhí)行完畢。
        [subscriber sendCompleted];

        return nil;
    }];

}];

// 強(qiáng)引用命令,不要被銷毀,否則接收不到數(shù)據(jù)
_conmmand = command;


// 3.執(zhí)行命令
[self.conmmand execute:@1];

// 4.訂閱RACCommand中的信號(hào)
[command.executionSignals subscribeNext:^(id x) {

    [x subscribeNext:^(id x) {

        NSLog(@"%@",x);
    }];

}];

// RAC高級(jí)用法
// switchToLatest:用于signal of signals,獲取signal of signals發(fā)出的最新信號(hào),也就是可以直接拿到RACCommand中的信號(hào)
[command.executionSignals.switchToLatest subscribeNext:^(id x) {

    NSLog(@"%@",x);
}];

// 5.監(jiān)聽命令是否執(zhí)行完畢,默認(rèn)會(huì)來一次,可以直接跳過,skip表示跳過第一次信號(hào)。
[[command.executing skip:1] subscribeNext:^(id x) {

    if ([x boolValue] == YES) {
        // 正在執(zhí)行
        NSLog(@"正在執(zhí)行");

    }else{
        // 執(zhí)行完成
        NSLog(@"執(zhí)行完成");
    }

}];
RACScheduler:RAC中的隊(duì)列,用GCD封裝的。
RACUnit :表?stream不包含有意義的值,也就是看到這個(gè),可以直接理解為nil.
RACEvent: 把數(shù)據(jù)包裝成信號(hào)事件(signal event)。它主要通過RACSignal的-materialize來使用,然并卵。
RACSubscriber:表示訂閱者的意思,用于發(fā)送信號(hào),這是一個(gè)協(xié)議,不是一個(gè)類,只要遵守這個(gè)協(xié)議,并且實(shí)現(xiàn)方法才能成為訂閱者。通過create創(chuàng)建的信號(hào),都有一個(gè)訂閱者,幫助他發(fā)送數(shù)據(jù)。

RACDisposable:用于取消訂閱或者清理資源,當(dāng)信號(hào)發(fā)送完成或者發(fā)送錯(cuò)誤的時(shí)候,就會(huì)自動(dòng)觸發(fā)它。

使用場(chǎng)景:不想監(jiān)聽某個(gè)信號(hào)時(shí),可以通過它主動(dòng)取消訂閱信號(hào)。
RACSubject:RACSubject:信號(hào)提供者,自己可以充當(dāng)信號(hào),又能發(fā)送信號(hào)。

使用場(chǎng)景:通常用來代替代理,有了它,就不必要定義代理了。
RACReplaySubject:重復(fù)提供信號(hào)類,RACSubject的子類。
* RACReplaySubject與RACSubject區(qū)別:
* RACReplaySubject可以先發(fā)送信號(hào),在訂閱信號(hào),RACSubject就不可以。
* 使用場(chǎng)景一:如果一個(gè)信號(hào)每被訂閱一次,就需要把之前的值重復(fù)發(fā)送一遍,使用重復(fù)提供信號(hào)類。
* 使用場(chǎng)景二:可以設(shè)置capacity數(shù)量來限制緩存的value的數(shù)量,即只緩充最新的幾個(gè)值。
RACTuple:元組類,類似NSArray,用來包裝值.
RACSequence:RAC中的集合類,用于代替NSArray,NSDictionary,可以使用它來快速遍歷數(shù)組和字典。
RACMulticastConnection:用于當(dāng)一個(gè)信號(hào),被多次訂閱時(shí),為了保證創(chuàng)建信號(hào)時(shí),避免多次調(diào)用創(chuàng)建信號(hào)中的block,造成副作用,可以使用這個(gè)類處理。
使用注意:RACMulticastConnection通過RACSignal的-publish或者-muticast:方法創(chuàng)建.
RACMulticastConnection簡單使用:

六、ReactiveCocoa開發(fā)中常見用法

1 代替代理:
rac_signalForSelector:用于替代代理。

[[self rac_signalForSelector:@selector(tableView:didSelectRowAtIndexPath:) fromProtocol:@protocol(UITableViewDelegate)] subscribeNext:^(id x) {
            
}];

2 代替KVO :

rac_valuesAndChangesForKeyPath:用于監(jiān)聽某個(gè)對(duì)象的屬性改變。

//方法1
[[self rac_valuesAndChangesForKeyPath:@"title" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {
        
}];
//方法2
[[self rac_valuesForKeyPath:@"title" observer:nil] subscribeNext:^(id x) {
        
}];
//方法3
[RACObserve(self, title) subscribeNext:^(id x) {
        
}];

3 監(jiān)聽事件:

rac_signalForControlEvents:用于監(jiān)聽某個(gè)事件。

按鈕點(diǎn)擊
[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
        
}];
手勢(shì)事件
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]init];
[tap.rac_gestureSignal subscribeNext:^(id x) {

}];

4 代替通知:

rac_addObserverForName:用于監(jiān)聽某個(gè)通知。

[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"name" object:nil] subscribeNext:^(id x) {
        
}];

5 監(jiān)聽文本框文字改變:

rac_textSignal:只要文本框發(fā)出改變就會(huì)發(fā)出這個(gè)信號(hào)。

[[self.textField rac_textSignal] subscribeNext:^(NSString * _Nullable x) {
     
}];

6 處理當(dāng)界面有多次請(qǐng)求時(shí),需要都獲取到數(shù)據(jù)時(shí),才能展示界面

rac_liftSelector:withSignalsFromArray:Signals:當(dāng)傳入的Signals(信號(hào)數(shù)組),每一個(gè)signal都至少sendNext過一次,就會(huì)去觸發(fā)第一個(gè)selector參數(shù)的方法。
使用注意:幾個(gè)信號(hào),參數(shù)一的方法就幾個(gè)參數(shù),每個(gè)參數(shù)對(duì)應(yīng)信號(hào)發(fā)出的數(shù)據(jù)。
7 定時(shí)器

//定時(shí)器
RACDisposable * disposable = [[RACSignal interval:1 onScheduler:[RACScheduler scheduler]] subscribeNext:^(id x) {
        
}];
//釋放定時(shí)器
[disposable dispose];

8.集合遍歷RACSequence

//默認(rèn)在子線程中遍歷
NSArray *numbers = @[@"1",@"2",@"3",@"4"];
[numbers.rac_sequence.signal subscribeNext:^(id x) {
  NSLog(@"%@",x);
}];
//放在主線程中遍歷
[[numbers.rac_sequence.signal deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(id  _Nullable x) {
        
}];

9.映射Map(生成新的值)flattenMap(生成新的信號(hào))

map(生成新的值)
NSArray * newNumbers = [numbers.rac_sequence map:^id(id value) {
    return  [NSString stringWithFormat:@"numbers: %@",value];
}].array;
flattenMap(生成新的信號(hào))
// 創(chuàng)建信號(hào)
RACSubject *subject = [RACSubject subject];
// 綁定信號(hào)
RACSignal *bindSignal = [subject flattenMap:^RACStream *(id value) {
// value: 就是源信號(hào)發(fā)送的內(nèi)容
// 返回信號(hào)用來包裝成修改內(nèi)容的值
return [RACReturnSignal return:value];

}];
// flattenMap中返回的是什么信號(hào),訂閱的就是什么信號(hào)(那么,x的值等于value的值,如果我們操縱value的值那么x也會(huì)隨之而變)
// 訂閱信號(hào)
[bindSignal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];

// 發(fā)送數(shù)據(jù)
[subject sendNext:@"123"];

9.延時(shí)執(zhí)行

throttle:延時(shí)調(diào)用block(subscribeNext)
[[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] throttle:5] subscribeNext:^(id x) {
        
}];
delay:延遲調(diào)用
[[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] delay:5] subscribeNext:^(id x) {
         
}];
timeout:超時(shí)后則不調(diào)用block
[[self.signal timeout:5 onScheduler:[RACScheduler scheduler]] subscribeNext:^(id x) {
}];
延時(shí)執(zhí)行
[[RACScheduler scheduler] after:[NSDate dateWithTimeIntervalSinceNow:3] schedule:^{
           
 }];

10.過濾

filter接受滿足條件的信號(hào)
[[numbers.rac_sequence.signal filter:^BOOL(id value) {
    return [value integerValue] > 3;
}] subscribeNext:^(id x) {
        
}];
skip跳過幾個(gè)信號(hào)
// skip:后邊傳入要跳過幾個(gè)信號(hào)
RACSubject *subject = [RACSubject subject];
[[subject skip:2] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
[subject sendNext:@1];
[subject sendNext:@2];
[subject sendNext:@3];
distinctUntilChanged新值與舊值不一樣則接收

11.RAC組合

combine場(chǎng)景:賬號(hào)和密碼都有值,登錄按鈕才可點(diǎn)擊
//reduce里的參數(shù)一定要和combineLatest數(shù)組里的一一對(duì)應(yīng)。
RACSignal *combinSignal = [RACSignal combineLatest:@[self.accountField.rac_textSignal, self.pwdField.rac_textSignal] reduce:^id(NSString *account, NSString *pwd){ NSLog(@"%@ %@", account, pwd);
  return @(account.length && pwd.length);
}];

RAC(self.loginBtn, enabled) = combinSignal;
merge多個(gè)信號(hào)合并成一個(gè)信號(hào),任何一個(gè)信號(hào)有新值就會(huì)調(diào)用
// 創(chuàng)建信號(hào)A
RACSubject *signalA = [RACSubject subject];
// 創(chuàng)建信號(hào)B
RACSubject *signalB = [RACSubject subject];
//組合信號(hào)
RACSignal *mergeSignal = [signalA merge:signalB];
// 訂閱信號(hào)
[mergeSignal subscribeNext:^(id x) {
  NSLog(@"%@", x);
}];
// 發(fā)送信號(hào)---交換位置則數(shù)據(jù)結(jié)果順序也會(huì)交換
[signalB sendNext:@"下部分"];
[signalA sendNext:@"上部分"];

concat:串行執(zhí)行,第一個(gè)信號(hào)必須要調(diào)用sendCompleted
// 創(chuàng)建信號(hào)A
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
  [subscriber sendNext:@"上部分?jǐn)?shù)據(jù)"];
  [subscriber sendCompleted]; // 必須要調(diào)用sendCompleted方法!
  return nil;
}];

// 創(chuàng)建信號(hào)B,
RACSignal *signalsB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
  [subscriber sendNext:@"下部分?jǐn)?shù)據(jù)"];
  return nil;
}];

// 創(chuàng)建組合信號(hào)
RACSignal *concatSignal = [signalA concat:signalsB];
// 訂閱組合信號(hào)
[concatSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];

七、ReactiveCocoa常見宏

1.RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于給某個(gè)對(duì)象的某個(gè)屬性綁定。

    // 只要文本框文字改變,就會(huì)修改label的文字
    RAC(self.labelView,text) = _textField.rac_textSignal;
  1. RACObserve(self, name):監(jiān)聽某個(gè)對(duì)象的某個(gè)屬性,返回的是信號(hào)。
[RACObserve(self.view, center) subscribeNext:^(id x) {
 
        NSLog(@"%@",x);
    }];

3 @weakify(Obj)和@strongify(Obj),一般兩個(gè)都是配套使用,解決循環(huán)引用問題.

八、RAC 怎么區(qū)分熱信號(hào)和冷信號(hào)的,怎么相互轉(zhuǎn)換的?

Hot Observables(熱信號(hào))和Cold Observables(冷信號(hào))的區(qū)別:

  •   Hot Observables是主動(dòng)的,盡管你并沒有訂閱事件,但是它會(huì)時(shí)刻推送,就像鼠標(biāo)移動(dòng);而Cold Observables是被動(dòng)的,只有當(dāng)你訂閱的時(shí)候,它才會(huì)發(fā)布消息。
    
  •   Hot Observables可以有多個(gè)訂閱者,是一對(duì)多,集合可以與訂閱者共享信息;而Cold Observables只能一對(duì)一,當(dāng)有不同的訂閱者,消息是重新完整發(fā)送
    

熱信號(hào)相當(dāng)于直播,冷信號(hào)相當(dāng)于點(diǎn)播
RACSubject及其子類是熱信號(hào)
RACSignal排除RACSubject類以外的是冷信號(hào)
冷信號(hào)與熱信號(hào)的本質(zhì)區(qū)別在于是否保持狀態(tài),冷信號(hào)的多次訂閱是不保持狀態(tài)的,而熱信號(hào)是保持狀態(tài)的
RACSignal和RACSubject雖然都是信號(hào),但是它們有一個(gè)本質(zhì)的區(qū)別: RACSubject會(huì)持有訂閱者(因?yàn)镽ACSubject是熱信號(hào),為了保證未來有事件發(fā)送的時(shí)候,訂閱者可以收到信息,所以需要對(duì)訂閱者保持狀態(tài),做法就是持有訂閱者),而RACSignal不會(huì)持有訂閱者


xteerorcompleted.png

Rimplenentation Rxcsubject.png
@implementation RACSignal (Subscription)

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCAssert(NO, @"This method must be overridden by subclasses");
    return nil;
}

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);
    
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    return [self subscribe:o];
}

@implementation RACSubject

#pragma mark Lifecycle

+ (instancetype)subject {
    return [[self alloc] init];
}

- (id)init {
    self = [super init];
    if (self == nil) return nil;

    _disposable = [RACCompoundDisposable compoundDisposable];
    _subscribers = [[NSMutableArray alloc] initWithCapacity:1];
    
    return self;
}

- (void)dealloc {
    [self.disposable dispose];
}

#pragma mark Subscription

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCParameterAssert(subscriber != nil);

    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
    subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];

    NSMutableArray *subscribers = self.subscribers;
    @synchronized (subscribers) {
        [subscribers addObject:subscriber];
    }
    
    return [RACDisposable disposableWithBlock:^{
        @synchronized (subscribers) {
            // Since newer subscribers are generally shorter-lived, search
            // starting from the end of the list.
            NSUInteger index = [subscribers indexOfObjectWithOptions:NSEnumerationReverse passingTest:^ BOOL (id<RACSubscriber> obj, NSUInteger index, BOOL *stop) {
                return obj == subscriber;
            }];

            if (index != NSNotFound) [subscribers removeObjectAtIndex:index];
        }
    }];
}

十、創(chuàng)建信號(hào)簡單調(diào)用

image.png

RACSignal

先從最簡單的RACSignal開始,我們先來看看它是怎么創(chuàng)建的

-(void)creatSigal{
   
   RACSignal *sigal=[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
       
       [subscriber sendNext:@"你個(gè)小樣"];
       
       return nil;
   }];
   
   [sigal subscribeNext:^(id  _Nullable x) {
      
       NSLog(@"傳遞的數(shù)據(jù)是:----%@",x);
   }];
   
}

上面就創(chuàng)建了一個(gè)信號(hào),并且訂閱信號(hào),還發(fā)送消息了。創(chuàng)建是成功了,但是它具體的原來還不知道啊。不要慌,我們點(diǎn)進(jìn)方法看看。

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    RACDynamicSignal *signal = [[self alloc] init];
    signal->_didSubscribe = [didSubscribe copy];
    return [signal setNameWithFormat:@"+createSignal:"];
}

它其實(shí)就是一個(gè)信號(hào),先初始化了RACDynamicSignal,然后下面_didSubscribe這個(gè)熟悉嘛,是不是有點(diǎn)像訂閱信號(hào),RACDynamicSignal持有了didSubscribe,最后返回了一個(gè)信號(hào)。這個(gè)方法看完了,有一個(gè)疑問,didSubscribe這個(gè)是用來干嘛的?接著往下看訂閱信號(hào)的方法

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);
    
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    return [self subscribe:o];
}

第一行很容易理解,就是創(chuàng)建RACSubscriber(訂閱者),點(diǎn)進(jìn)去看看它里面的實(shí)現(xiàn)

@interface RACSubscriber ()

// These callbacks should only be accessed while synchronized on self.
@property (nonatomic, copy) void (^next)(id value);
@property (nonatomic, copy) void (^error)(NSError *error);
@property (nonatomic, copy) void (^completed)(void);

@property (nonatomic, strong, readonly) RACCompoundDisposable *disposable;

@end

@implementation RACSubscriber

#pragma mark Lifecycle

+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
    RACSubscriber *subscriber = [[self alloc] init];

    subscriber->_next = [next copy];
    subscriber->_error = [error copy];
    subscriber->_completed = [completed copy];

    return subscriber;
}

我們發(fā)現(xiàn),它初始化了訂閱者,然后訂閱者持有了next這個(gè)block,持有了error、completed,很簡單。點(diǎn)進(jìn)去看[self subscribe:o];的實(shí)現(xiàn)

  • (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCParameterAssert(subscriber != nil);

    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
    subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
    //看的有點(diǎn)懵,上面的對(duì)象都不知道是什么 但是didSubscribe這個(gè)熟悉啊,在創(chuàng)建信號(hào)的時(shí)候RACDynamicSignal持有didSubscribe

    if (self.didSubscribe != NULL) {
        RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
//這里調(diào)用didSubscribe方法,并且把剛才傳入的subscriber調(diào)用出去
            RACDisposable *innerDisposable = self.didSubscribe(subscriber);
            [disposable addDisposable:innerDisposable];
        }];

        [disposable addDisposable:schedulingDisposable];
    }
    
    return disposable;
}

現(xiàn)在我們最開始的疑問解開了,didSubscribe的調(diào)用。這個(gè)方法里能看懂的就是,先判斷有沒有didSubscribe,有的話就執(zhí)行這個(gè)方法,參數(shù)是subscriber訂閱者。后面我們?cè)谡fRACDisposable。

- (void)sendNext:(id)value {
    @synchronized (self) {
        void (^nextBlock)(id) = [self.next copy];
        if (nextBlock == nil) return;

        nextBlock(value);
    }
}

上面是訂閱者發(fā)送消息的方法實(shí)現(xiàn),判斷有沒有nextblock,而這里的nextblock也正是subscriber->_next = [next copy];保存的。如果有 就把發(fā)送的消息,傳遞出去。
上面我們一個(gè)一個(gè)的都點(diǎn)進(jìn)去看了他們的具體實(shí)現(xiàn),現(xiàn)在來張圖更加直觀的看看這個(gè)過程


image.png

這里面有要說明的是:didSubscribe的參數(shù)是RACSubscriber,而在我們發(fā)送消息的時(shí)候, [subscriber sendNext:@"你個(gè)小樣"]; 這里的訂閱者就是didSubscribe傳入的,而訂閱者持有nextblock,所以nextBlock(value);消息就傳入到訂閱的block中了。這就是創(chuàng)建RACSignal的一個(gè)流程。

RACSubject

-(void)creatracSubject{
    
    RACSubject *subject=[RACSubject subject];
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"收到的消息是:---%@",x);
    }];
    //發(fā)送消息
    [subject sendNext:@"666 666"];
    
}
 LBDaySurgery(Dev)[10505:3899054] 收到的消息是:---666 666

我們點(diǎn)進(jìn)方法看看它的實(shí)現(xiàn)原理

+ (instancetype)subject {
    return [[self alloc] init];
}
- (instancetype)init {
    self = [super init];
    if (self == nil) return nil;

    _disposable = [RACCompoundDisposable compoundDisposable];
    _subscribers = [[NSMutableArray alloc] initWithCapacity:1];
    
    return self;
}

創(chuàng)建subject對(duì)象時(shí),初始化了一個(gè)取消信號(hào)和一個(gè)數(shù)組,從名字我們能看出這是用來存放訂閱者的。
接著看訂閱信號(hào)的實(shí)現(xiàn)

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);
    
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    return [self subscribe:o];
}
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCParameterAssert(subscriber != nil);

    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
    subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];

    NSMutableArray *subscribers = self.subscribers;
    @synchronized (subscribers) {
        [subscribers addObject:subscriber];
    }
    
    [disposable addDisposable:[RACDisposable disposableWithBlock:^{
        @synchronized (subscribers) {
            // Since newer subscribers are generally shorter-lived, search
            // starting from the end of the list.
            NSUInteger index = [subscribers indexOfObjectWithOptions:NSEnumerationReverse passingTest:^ BOOL (id<RACSubscriber> obj, NSUInteger index, BOOL *stop) {
                return obj == subscriber;
            }];

            if (index != NSNotFound) [subscribers removeObjectAtIndex:index];
        }
    }]];

    return disposable;
}

從上面這段代碼能看到,是把訂閱者都加入到subscribers數(shù)組中了。也表示subject強(qiáng)引用subscriber
我們?cè)倏窗l(fā)布消息的實(shí)現(xiàn)

- (void)enumerateSubscribersUsingBlock:(void (^)(id<RACSubscriber> subscriber))block {
    NSArray *subscribers;
    @synchronized (self.subscribers) {
        subscribers = [self.subscribers copy];
    }
//遍歷subscribers中的訂閱者,然后block傳遞subscriber
    for (id<RACSubscriber> subscriber in subscribers) {
        block(subscriber);
    }
}

#pragma mark RACSubscriber

- (void)sendNext:(id)value {
    [self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
//這里和之前RACSignal發(fā)送消息相同
        [subscriber sendNext:value];
    }];
}

1.創(chuàng)建的subject內(nèi)部會(huì)創(chuàng)建數(shù)組_subscribers,用來保存所有的訂閱者。
2.訂閱信息的時(shí)候會(huì)創(chuàng)建訂閱者
3.發(fā)送消息的時(shí)候,會(huì)依次發(fā)送。

九、RAC坑

鏈接

RAC循環(huán)調(diào)用問題/RAC內(nèi)存泄漏問題

創(chuàng)建信號(hào)引起的循環(huán)引用情況

如下景幾種簡單的創(chuàng)建信號(hào)時(shí)引起的街環(huán)引用情況:.png

RACObserve引發(fā)

- (void)viewDidLoad
{
    [super viewDidLoad];
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { //1
        MTModel *model = [[MTModel alloc] init]; // MTModel有一個(gè)名為的title的屬性
        [subscriber sendNext:model];
        [subscriber sendCompleted];
        return nil;
    }];
    self.flattenMapSignal = [signal flattenMap:^RACStream *(MTModel *model) { //2
        return RACObserve(model, title);
    }];
    [self.flattenMapSignal subscribeNext:^(id x) { //3
        NSLog(@"subscribeNext - %@", x);
    }];
}
define RACObserve(TARGET, KEYPATH) \
    ({ \
        _Pragma("clang diagnostic push") \
        _Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \
        __weak id target_ = (TARGET); \
        [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \
        _Pragma("clang diagnostic pop") \
    })
image.png

意:2是間接持有,從邏輯上來講,flattenMapSignal會(huì)有一個(gè)didSubscribeBlock,為了讓傳遞給flattenMap操作的block有意義,didSubscribeBlock會(huì)對(duì)該block進(jìn)行持有,從而也就間接持有了self,感興趣的讀者可以去看下相關(guān)源碼。
OK,找到了問題所在,解決起來也就簡單了,使用@weakify和@strongify

RACSubject引起的循環(huán)

- (void)viewDidLoad {
    [super viewDidLoad];
    RACSubject *subject = [RACSubject subject]; //1
    [subject.rac_willDeallocSignal subscribeCompleted:^{ //2
        NSLog(@"subject dealloc");
    }];
    [subject subscribeNext:^(id x) { //3
        NSLog(@"next = %@", x);
    }];
    [subject sendNext:@1]; //4
}

1.創(chuàng)建一個(gè)RACSubject的實(shí)例;
2.訂閱subject的dealloc信號(hào),在subject被釋放的時(shí)候會(huì)發(fā)送完成信號(hào);
3.訂閱subject;
4.使用subject發(fā)送一個(gè)值。

2016-06-13 09:15:25.426 RAC[5366:245360] next = 1
2016-06-13 09:15:25.428 RAC[5366:245360] subject dealloc

工作相當(dāng)良好,接下來改造下程序,要求對(duì)subject發(fā)送的所有值進(jìn)行乘3,這用map很容易就實(shí)現(xiàn)了。

- (void)viewDidLoad {
    [super viewDidLoad];
    RACSubject *subject = [RACSubject subject]; 
    [subject.rac_willDeallocSignal subscribeCompleted:^{ 
        NSLog(@"subject dealloc");
    }];
    
    [[subject map:^id(NSNumber *value) { 
        return @([value integerValue] * 3);
    }] subscribeNext:^(id x) { 
        NSLog(@"next = %@", x);
    }];
    [subject sendNext:@1]; 
}

跟之前大體不變,只是對(duì)subject進(jìn)行了map操作然后再訂閱,看下輸出結(jié)果:

2016-06-13 09:21:42.450 RAC[5404:248584] next = 3

同樣代碼將RACSubject換成RACSignal是可以釋放掉的
雖然得出了結(jié)論,但是留下的疑問也是不少,如果你希望知道這其中的緣由,請(qǐng)繼續(xù)往下看。 簡單來說,留下的疑問有:

1.為什么對(duì)RACSubject的實(shí)例進(jìn)行map操作之后會(huì)產(chǎn)生內(nèi)存泄漏?
2.為什么RACSignal不管是否有map操作,都不會(huì)產(chǎn)生內(nèi)存泄漏?
3.針對(duì)第一個(gè)問題,為什么發(fā)送完成可以修復(fù)內(nèi)存泄漏?
源碼分析

1.創(chuàng)建信號(hào)量   RACSubject *subject = [RACSubject subject]; 
@implementation RACSubject

#pragma mark Lifecycle

+ (instancetype)subject {
  return [[self alloc] init];
}

- (id)init {
  self = [super init];
  if (self == nil) return nil;

  _disposable = [RACCompoundDisposable compoundDisposable];
  _subscribers = [[NSMutableArray alloc] initWithCapacity:1];
  
  return self;
}
2.[[subject map:^id(NSNumber *value) {
//將@([value integerValue] * 3)傳給RACReturnSignal->_value
          return @([value integerValue] * 3);
      }] subscribeNext:^(id x) {
          NSLog(@"next = %@", x);
      }];
@implementation RACStream (Operations)
- (instancetype)map:(id (^)(id value))block {
  NSCParameterAssert(block != nil);

  Class class = self.class;
  return [[self flattenMap:^(id value) {
      //block(value)執(zhí)行返回結(jié)果 @([value integerValue] * 3);
      return [class return:block(value)];
  }] setNameWithFormat:@"[%@] -map:", self.name];
}
- (instancetype)flattenMap:(RACStream * (^)(id value))block {
  Class class = self.class;
  
/**

RACSignal *newSignal = [orgSignal bind:^RACStreamBindBlock{
   RACStreamBindBlock bindBlock = ^(id value, BOOL *stop) {
       RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
           [subscriber sendNext:@(2*[value intValue])];
           [subscriber sendCompleted];
           return [RACDisposable disposableWithBlock:^{
               JCLogD(@"Bind disposed");
           }];
       }];
       return signal;
   };
   return bindBlock;
}];
*/
  //傳遞一個(gè)(RACStreamBindBlock (^)(void))類型block
  return [[self bind:^{
      //返回RACStreamBindBlock類型block
      return ^(id value, BOOL *stop) {
          /**
           調(diào)用flattenMap時(shí)候傳進(jìn)來的block是這個(gè):
           ^(id value) {
                   return [class return:block(value)];
               }
           id stream = block(value)執(zhí)行 return [class return:block(value)];
           RACReturnSignal *signal = [[self alloc] init];
           signal->_value = value;
           stream就是RACReturnSignal類型signal
           */
          //block執(zhí)行了返回一個(gè)RACStream類型信號(hào)
          id stream = block(value) ?: [class empty];
          NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);

          return stream;
      };
  }] setNameWithFormat:@"[%@] -flattenMap:", self.name];
}
@implementation RACSignal (RACStream)
- (RACSignal *)bind:(RACStreamBindBlock (^)(void))block {
  NSCParameterAssert(block != NULL);

  /*
   * -bind: should:
   * 
   * 1. Subscribe to the original signal of values.
   * 2. Any time the original signal sends a value, transform it using the binding block.
   * 3. If the binding block returns a signal, subscribe to it, and pass all of its values through to the subscriber as they're received.
   * 4. If the binding block asks the bind to terminate, complete the _original_ signal.
   * 5. When _all_ signals complete, send completed to the subscriber.
   * 
   * If any signal sends an error at any point, send that to the subscriber.
   */
//創(chuàng)建一個(gè)signal給外界
  return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
      //執(zhí)行block,返回RACStreamBindBlock類型block
      /**
       - (instancetype)flattenMap:(RACStream * (^)(id value))block
       RACStreamBindBlock bindingBlock = block();執(zhí)行block返回RACStreamBindBlock類型
       以下就是bindingBlock賦值
       return ^(id value, BOOL *stop) {
           //block執(zhí)行了返回一個(gè)RACStream類型信號(hào)
           id stream = block(value) ?: [class empty];
           NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);

           return stream;
       };
       */
      RACStreamBindBlock bindingBlock = block();

      NSMutableArray *signals = [NSMutableArray arrayWithObject:self];

      RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];

      void (^completeSignal)(RACSignal *, RACDisposable *) = ^(RACSignal *signal, RACDisposable *finishedDisposable) {
          BOOL removeDisposable = NO;

          @synchronized (signals) {
              [signals removeObject:signal];

              if (signals.count == 0) {
                  [subscriber sendCompleted];
                  [compoundDisposable dispose];
              } else {
                  removeDisposable = YES;
              }
          }

          if (removeDisposable) [compoundDisposable removeDisposable:finishedDisposable];
      };

      void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
          @synchronized (signals) {
              [signals addObject:signal];
          }

          RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
          [compoundDisposable addDisposable:selfDisposable];
//訂閱新建的信號(hào)RACReturnSignal
          RACDisposable *disposable = [signal subscribeNext:^(id x) {
              //這里value直接[subscriber sendNext:self.value];也就將外界value傳過來
              [subscriber sendNext:x];
          } error:^(NSError *error) {
              [compoundDisposable dispose];
              [subscriber sendError:error];
          } completed:^{
              @autoreleasepool {
                  completeSignal(signal, selfDisposable);
              }
          }];

          selfDisposable.disposable = disposable;
      };

      @autoreleasepool {
          RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
          [compoundDisposable addDisposable:selfDisposable];
//源信號(hào)量訂閱信息
          RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
              // Manually check disposal to handle synchronous errors.
              if (compoundDisposable.disposed) return;

              BOOL stop = NO;
              
              //這里返回一個(gè)信號(hào),執(zhí)行了 block(value)
              /**
               
               - (instancetype)flattenMap:(RACStream * (^)(id value))block
               signal = bindingBlock(x, &stop);表達(dá)式就是以下這個(gè)方法
               id stream = block(value) ?: [class empty];
               
               - (instancetype)map:(id (^)(id value))block
               這里block就是=map方法里^(id value) {
               //block(value)執(zhí)行返回結(jié)果 @([value integerValue] * 3);
               return [class return:block(value)];
              而 stream=重新創(chuàng)建一個(gè)RACReturnSignal類型信號(hào)返回給外界
           }
               */
              //  x就是代表[[subject map:^id(NSNumber *value) 的value值,比如[subject sendNext:@1];表示value就是1
              id signal = bindingBlock(x, &stop);

              @autoreleasepool {
                  //這里傳入RACReturnSignal類型信號(hào),新建的
                  if (signal != nil) addSignal(signal);
                  if (signal == nil || stop) {
                      [selfDisposable dispose];
                      completeSignal(self, selfDisposable);
                  }
              }
          } error:^(NSError *error) {
              [compoundDisposable dispose];
              [subscriber sendError:error];
          } completed:^{
              @autoreleasepool {
                  completeSignal(self, selfDisposable);
              }
          }];

          selfDisposable.disposable = bindingDisposable;
      }

      return compoundDisposable;
  }] setNameWithFormat:@"[%@] -bind:", self.name];
}

OK,了解了bind操作的用途,也是時(shí)候回歸主題了——內(nèi)存是怎么泄露的。 首先我們看到,在didSubscribe的開頭,就創(chuàng)建了一個(gè)數(shù)組signals,并且持有了self,也就是源信號(hào)

NSMutableArray *signals = [NSMutableArray arrayWithObject:self];

接下來會(huì)對(duì)源信號(hào)進(jìn)行訂閱:

RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
                // Manually check disposal to handle synchronous errors.
                if (compoundDisposable.disposed) return;

                BOOL stop = NO;
                id signal = bindingBlock(x, &stop);

                @autoreleasepool {
                    if (signal != nil) addSignal(signal);
                    if (signal == nil || stop) {
                        [selfDisposable dispose];
                        completeSignal(self, selfDisposable);
                    }
                }
            } error:^(NSError *error) {
                [compoundDisposable dispose];
                [subscriber sendError:error];
            } completed:^{
                @autoreleasepool {
                    completeSignal(self, selfDisposable);
                }
            }];

訂閱者會(huì)持有nextBlock、errorBlock、completedBlock三個(gè)block,為了簡單,我們只討論nextBlock。 從nextBlock中的completeSignal(self, selfDisposable);這一行代碼可以看出,nextBlock對(duì)self,也就是源信號(hào)進(jìn)行了持有,再看到if (signal != nil) addSignal(signal);這一行,nextBlock對(duì)addSignal進(jìn)行了持有,addSignal是在訂閱self之前定義的一個(gè)block。

void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
            @synchronized (signals) {
                [signals addObject:signal];
            }

            RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
            [compoundDisposable addDisposable:selfDisposable];

            RACDisposable *disposable = [signal subscribeNext:^(id x) {
                [subscriber sendNext:x];
            } error:^(NSError *error) {
                [compoundDisposable dispose];
                [subscriber sendError:error];
            } completed:^{
                @autoreleasepool {
                    completeSignal(signal, selfDisposable);
                }
            }];

            selfDisposable.disposable = disposable;
        };

addSignal這個(gè)block里面對(duì)一開始創(chuàng)建的數(shù)組signals進(jìn)行了持有,用一幅圖來描述下剛才所說的關(guān)系:

image.png

如果這個(gè)signal是一個(gè)RACSignal,那么是沒有任何問題的;如果是signal是一個(gè)RACSubject,那問題就來了。還記得前面說過的RACSignal和RACSubject的區(qū)別嗎?RACSubject會(huì)持有訂閱者,而RACSignal不會(huì)持有訂閱者,如果signal是一個(gè)RACSubject,那么圖應(yīng)該是這樣的:
image.png

很明顯,產(chǎn)生了循環(huán)引用?。?!到這里,也就解答了前面提出的三個(gè)問題的前兩個(gè): 對(duì)一個(gè)信號(hào)進(jìn)行了map操作,那么最終會(huì)調(diào)用到bind。 如果源信號(hào)是RACSubject,由于RACSubject會(huì)持有訂閱者,所以產(chǎn)生了循環(huán)引用(內(nèi)存泄漏); 如果源信號(hào)是RACSignal,由于RACSignal不會(huì)持有訂閱者,那么也就不存在循環(huán)引用。
image.png

鏈接

還剩下最后一個(gè)問題:如果源信號(hào)是RACSubject,為什么發(fā)送完成可以修復(fù)內(nèi)存泄漏? 來看下訂閱者收到完成信號(hào)之后干了些什么:

RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
    //...
} error:^(NSError *error) {
    //...
} completed:^{
    @autoreleasepool {
        completeSignal(self, selfDisposable);
    }
}];

很簡單,只是調(diào)用了一下completeSignal這個(gè)block。再看下這個(gè)block內(nèi)部在干嘛:

void (^completeSignal)(RACSignal *, RACDisposable *) = ^(RACSignal *signal, RACDisposable *finishedDisposable) {
    BOOL removeDisposable = NO;
    @synchronized (signals) {
        [signals removeObject:signal]; //1
        if (signals.count == 0) {
            [subscriber sendCompleted]; //2
            [compoundDisposable dispose]; //3
        } else {
            removeDisposable = YES;
        }
    }
    if (removeDisposable) [compoundDisposable removeDisposable:finishedDisposable]; //4
};

//1這里從signals這個(gè)數(shù)組中移除傳入的signal,也就斷掉了signals持有subject這條線。 //2、//3、//4其實(shí)干的事情差不多,都是拿到對(duì)應(yīng)的disposable調(diào)用dispose,這樣資源就得到了回收,subject就不會(huì)再持有subscriber,subscriber也會(huì)對(duì)自己的nextBlock、errorBlock、completedBlock三個(gè)block置為nil,就不會(huì)存在引用關(guān)系,所有的對(duì)象都得到了釋放。 有興趣的同學(xué)可以去了解下RACDisposable,它也是ReactiveCocoa中的重要一員,對(duì)理解源碼有很大的幫助。 map只是一個(gè)很典型的操作,其實(shí)在ReactiveCocoa的實(shí)現(xiàn)中,幾乎所有的操作底層都會(huì)調(diào)用到bind這樣一個(gè)方法,包括但不限于: map、filter、merge、combineLatest、flattenMap ……

所以在使用ReactiveCocoa的時(shí)候也一定要仔細(xì),對(duì)信號(hào)操作完成之后,記得發(fā)送完成信號(hào),不然可能在不經(jīng)意間就導(dǎo)致了內(nèi)存泄漏。

RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    [subscriber sendNext:@1];
    [subscriber sendCompleted]; // 保證源信號(hào)發(fā)送完成
    return nil;
}];

RACSignal *replaySignal = [signal replay]; // 這里返回的其實(shí)是一個(gè)RACReplaySubject

[[replaySignal map:^id(NSNumber *value) {
    return @([value integerValue] * 3);
}] subscribeNext:^(id x) {
    NSLog(@"subscribeNext - %@", x);
}];

總之,一句話:使用ReactiveCocoa必須要保證信號(hào)發(fā)送完成或者發(fā)送錯(cuò)誤。

補(bǔ)充map方法

對(duì)于研究RAC框架來說,map方法比較晦澀難懂,自己也是看源碼半天來的
接下來直接看方法,主要是看方法參數(shù)block

/// A block which accepts a value from a RACStream and returns a new instance
/// of the same stream class.
///
/// Setting `stop` to `YES` will cause the bind to terminate after the returned
/// value. Returning `nil` will result in immediate termination.
typedef RACStream * (^RACStreamBindBlock)(id value, BOOL *stop);

- (instancetype)map:(id (^)(id value))block 
- (instancetype)flattenMap:(RACStream * (^)(id value))block
- (RACSignal *)bind:(RACStreamBindBlock (^)(void))block

分析

1.然后在bind方法直接第一次調(diào)用block,返回的RACStreamBindBlock
RACStreamBindBlock bindingBlock = block();
執(zhí)行過后就是flattenMap方法里:^(id value, BOOL *stop) {
            /**
             調(diào)用flattenMap時(shí)候傳進(jìn)來的block是這個(gè):
             ^(id value) {
                     return [class return:block(value)];
                 }
             id stream = block(value)執(zhí)行 return [class return:block(value)];
             RACReturnSignal *signal = [[self alloc] init];
             signal->_value = value;
             stream就是RACReturnSignal類型signal
             */
            //block執(zhí)行了返回一個(gè)RACStream類型信號(hào)
            id stream = block(value) ?: [class empty];
            NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);

            return stream;
        };
2.在bind方法里源信號(hào)的didSubscribeblock里執(zhí)行第二次block()
id signal = bindingBlock(x, &stop);
從上面block里看bindingBlock就是flattenMap方法里block的block,也就是:外界調(diào)用flattenMap傳過來的block參數(shù)
也就是bindingBlock =^(id value) {
        //block(value)執(zhí)行返回結(jié)果 @([value integerValue] * 3);
        return [class return:block(value)];
    }
3,id  signal = [class return:block(value)] 創(chuàng)建RACReturnSignal類型的signal
4,value就是從bind里面參數(shù)傳回來的

map:方法實(shí)現(xiàn)方案是將block一個(gè)類型傳給下一個(gè)方法,上一個(gè)方法參數(shù)類型同時(shí)也是下一個(gè)方法調(diào)用參數(shù)block類型返回值類型,最后在bind方法直接幾次(有幾次調(diào)用,就幾次調(diào)用block,比如這次map方法最終調(diào)用到bind是兩次,而再bind方法直接幾次bloc)block,最終執(zhí)行最初的map傳過去block,通過block將最里面的參數(shù)傳外界

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

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

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