ReactiveCocoa核心方法bind

ReactiveCocoa操作
所有的信號(RACSignal)都可以進行操作處理汽纤,因為所有的操作方法都定義在RACStream.h中艾帐,而RACSignal繼承RACStream威根。
ReactiveCocoa操作思想
運用的是(鉤子)HooK思想纬傲,Hook是一種用于改變API執(zhí)行結(jié)果的技術(shù)。
Hook用處:截獲API調(diào)用的技術(shù)。
Hook原理:在每次調(diào)用一個API返回結(jié)果之前,先執(zhí)行自己的方法,改變方法的輸出晶丘。
RAC開發(fā)方式:RAC核心開發(fā)方式也就是綁定,之前的開發(fā)方式是賦值唐含,而RAC開發(fā)應該把重心放在綁定浅浮,也就是在創(chuàng)建一個對象的時候就綁定好以后想要做的事情,而不是等賦值之后在去做事情捷枯。
例如:把數(shù)據(jù)展示在控件上滚秩,之前都是重寫控件的setModel方法,用RAC就可以在一開始創(chuàng)建控件的時候就綁定好數(shù)據(jù)

ReactiveCocoa核心方法bind
ReactiveCocoa操作的核心方法是bind(綁定)淮捆,給RAC中的信號進行綁定郁油,只要信號一發(fā)送數(shù)據(jù)就能監(jiān)聽到,從而把發(fā)送數(shù)據(jù)改成自己想要的數(shù)據(jù)攀痊。
在開發(fā)中很少使用bind方法桐腌,bind屬于RAC中的底層方法,RAC已經(jīng)封裝了很多好用的其他方法苟径,底層都是調(diào)用bind,用法比bind簡單案站。
bind方法簡單介紹和使用:
// 創(chuàng)建信號

  1. RACSubject *subject = [RACSubject subject];
    // 綁定信號,只有綁定的信號被訂閱就會被調(diào)用
    // bind 返回一個綁定信號
  2. RACSignal *bindsignal = [subject bind:^RACStreamBindBlock{
    return ^RACSignal *(id value, BOOL *stop){
        // block調(diào)用:只要原信號發(fā)送數(shù)據(jù),就會調(diào)用block
        // block作用:處理原信號內(nèi)容
        // value:原信號發(fā)送的內(nèi)容
        
        NSLog(@"接收到原信號的內(nèi)容%@",value);
        // 返回信號,不能傳nil,返回空信號[RACSignal empty]
        value = @"程倩";
        return [RACReturnSignal return:value];
    };
    
}];
  1. [bindsignal subscribeNext:^(id x) {
    NSLog(@"接收到綁定信號處理完的內(nèi)容%@",x);
    }];
    // 原型號發(fā)送數(shù)據(jù)
  2. [subject sendNext:@"123"];
    輸出:
    2016-05-17 11:41:00.549 ReactiveCocoaTest1[1102:67222] 接收到原信號的內(nèi)容123
    2016-05-17 11:41:00.549 ReactiveCocoaTest1[1102:67222] 接收到綁定信號處理完的內(nèi)容程倩

分析

  1. RACSubject *subject = [RACSubject subject];
    

    創(chuàng)建一個RACSubject原信號 初始化一個數(shù)組_subscribers
    源碼:
    @implementation RACSubject

      #pragma mark Lifecycle
    
    • (instancetype)subject {
      return [[self alloc] init];
      }
    • (id)init {
      self = [super init];
      if (self == nil) return nil;
      //初始化一個訂閱者和_subscribers數(shù)組
      _disposable = [RACCompoundDisposable compoundDisposable];
      _subscribers = [[NSMutableArray alloc] initWithCapacity:1];

      return self;
      }

  2. RACSignal *bindsignal = [subject bind:^RACStreamBindBlock{
    return ^RACSignal (id value, BOOL stop){
    NSLog(@"接收到原信號的內(nèi)容%@",value);
    // 返回信號,不能傳nil,返回空信號[RACSignal empty]
    value = @"程倩";
    return [RACReturnSignal return:value];
    };
    }];
    bind:這個函數(shù)存在于RACSubject的父類RACSignal中
    源碼:
    /

  • @param block 傳入的block
  • @return 返回綁定后的信號
    */
  • (RACSignal *)bind:(RACStreamBindBlock (^)(void))block {
    NSCParameterAssert(block != NULL);// 一個異常處理棘街,不太懂
 // 直接返回了一個綁定后的信號 創(chuàng)建一個新的信號返回蟆盐,創(chuàng)建的是(RACDynamicSignal類型信號)
// RACDynamicSignal是RACSignal的子類 將這個didSubscribe block塊保存在了RACDynamicSignal中的_didSubscribe block中

{
可以看到 [RACSignal createSignal:]中返回的結(jié)果是[RACDynamicSignal createSignal:didSubscribe];
看一下[RACDynamicSignal createSignal:didSubscribe]中的源碼:
@implementation RACDynamicSignal

  • (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    RACDynamicSignal *signal = [[self alloc] init];
    signal->_didSubscribe = [didSubscribe copy];
    return [signal setNameWithFormat:@"+createSignal:"];
    }
    返回的是RACDynamicSignal對象,并且將傳入的block塊保存在了對象的_didSubscribe中
    }

// 所以說這個block現(xiàn)在并沒有被執(zhí)行遭殉,只是將這個block存在了綁定信號中了
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
//當綁定的信號被訂閱的時候開始執(zhí)行這個block
//調(diào)用block后得到創(chuàng)建綁定信號時傳入block返回的block 這個block返回一個新的信號
RACStreamBindBlock bindingBlock = block();

    NSMutableArray *signals = [NSMutableArray arrayWithObject:self];

    RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];

           // 這個block暫時不會被執(zhí)行
    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];
    };

     // 這個block暫時不會被執(zhí)行
    void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
        @synchronized (signals) {
            [signals addObject:signal];
        }

        RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
        [compoundDisposable addDisposable:selfDisposable];
       // 在這里對返回的RACReturnSignal內(nèi)容處理完成的信號進行訂閱
        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;
    };

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

               //當代碼執(zhí)行到這里的時候
      // 在這里對原信號進行訂閱石挂,當原信號發(fā)送數(shù)據(jù)的時候會調(diào)用這個Nextblock
        RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
            
            if (compoundDisposable.disposed) return;

            BOOL stop = NO;

// 當原信號發(fā)送數(shù)據(jù)的時候在這里調(diào)用了之前綁定block返回的block內(nèi)容,得到一個信號
id signal = bindingBlock(x, &stop);

            @autoreleasepool {

// 這這里執(zhí)行了addSignal block,傳入綁定block執(zhí)行完畢返回的信號
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];

}

3.訂閱綁定信號:

[bindsignal subscribeNext:^(id x) {
NSLog(@"接收到綁定信號處理完的內(nèi)容%@",x);
}];
分析:
subscribeNext:函數(shù)源碼
bindsignal的類型是RACDynamicSignal類型

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

  • (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCParameterAssert(subscriber != nil);
    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
    // 這個函數(shù)里面記錄了綁定信號的訂閱者
    源碼{
    - (instancetype)initWithSubscriber:(id<RACSubscriber>)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable {
    NSCParameterAssert(subscriber != nil);

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

    // 記錄下了綁定信號的訂閱者
    _innerSubscriber = subscriber;
    _signal = signal;
    _disposable = disposable;

    [self.innerSubscriber didSubscribeWithDisposable:self.disposable];
    return self;
    }

pragma mark RACSubscriber

  • (void)sendNext:(id)value {
    if (self.disposable.disposed) return;

    if (RACSIGNAL_NEXT_ENABLED()) {
    RACSIGNAL_NEXT(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString([value description]));
    }
    // 在這里拿到綁定后的信號訂閱者發(fā)送數(shù)據(jù)
    [self.innerSubscriber sendNext:value];
    }

    }
    subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
    if (self.didSubscribe != NULL) {
    RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
    // 在這里可以看到訂閱綁定信號的時候調(diào)用了創(chuàng)建綁定信號時候傳入的block 跳轉(zhuǎn)到2步驟中的 block
    RACDisposable *innerDisposable = self.didSubscribe(subscriber);
    [disposable addDisposable:innerDisposable];
    }];
    [disposable addDisposable:schedulingDisposable];
    }
    return disposable;
    }

  1. // 原型號發(fā)送數(shù)據(jù)
    [subject sendNext:@"123"];
    分析:
    拿到數(shù)組中的所有的block調(diào)用恩沽,(原信號被訂閱時候存下的block)

例子:
假設想監(jiān)聽文本框的內(nèi)容誊稚,并且在每次輸出結(jié)果的時候翔始,都在文本框的內(nèi)容拼接一段文字“輸出:”

  // 方式一:在返回結(jié)果后罗心,拼接。
[_textfield.rac_textSignal subscribeNext:^(id x) {
    NSLog(@"輸出:%@",x);
}];

// 方式二:在返回結(jié)果前城瞎,拼接渤闷,使用RAC中bind方法做處理。
// bind方法參數(shù):需要傳入一個返回值是RACStreamBindBlock的block參數(shù)
// RACStreamBindBlock是一個block的類型脖镀,返回值是信號飒箭,參數(shù)(value,stop),因此參數(shù)的block返回值也是一個block。

// RACStreamBindBlock:
// 參數(shù)一(value):表示接收到信號的原始值弦蹂,還沒做處理
// 參數(shù)二(*stop):用來控制綁定Block肩碟,如果*stop = yes,那么就會結(jié)束綁定。
// 返回值:信號凸椿,做好處理削祈,在通過這個信號返回出去,一般使用RACReturnSignal,需要手動導入頭文件RACReturnSignal.h脑漫。

// bind方法使用步驟:
// 1.傳入一個返回值RACStreamBindBlock的block髓抑。
// 2.描述一個RACStreamBindBlock類型的bindBlock作為block的返回值。
// 3.描述一個返回結(jié)果的信號优幸,作為bindBlock的返回值吨拍。
// 注意:在bindBlock中做信號結(jié)果的處理。

// 底層實現(xiàn):
// 1.源信號調(diào)用bind,會重新創(chuàng)建一個綁定信號网杆。
// 2.當綁定信號被訂閱羹饰,就會調(diào)用綁定信號中的didSubscribe,生成一個bindingBlock碳却。
// 3.當源信號有內(nèi)容發(fā)出严里,就會把內(nèi)容傳遞到bindingBlock處理,調(diào)用bindingBlock(value,stop)
// 4.調(diào)用bindingBlock(value,stop)追城,會返回一個內(nèi)容處理完成的信號(RACReturnSignal)刹碾。
// 5.訂閱RACReturnSignal,就會拿到綁定信號的訂閱者座柱,把處理完成的信號內(nèi)容發(fā)送出來迷帜。

// 注意:不同訂閱者,保存不同的nextBlock色洞,看源碼的時候戏锹,一定要看清楚訂閱者是哪個。
// 這里需要手動導入#import <ReactiveCocoa/RACReturnSignal.h>火诸,才能使用RACReturnSignal锦针。

[[_textfield.rac_textSignal bind:^RACStreamBindBlock{
    // 什么時候調(diào)用:
    // block作用:表示綁定了一個信號.
    return ^RACStream *(id value, BOOL *stop){
        // 什么時候調(diào)用block:當信號有新的值發(fā)出,就會來到這個block置蜀。
        // block作用:做返回值的處理
        // 做好處理奈搜,通過信號返回出去.
        return [RACReturnSignal return:[NSString stringWithFormat:@"輸出:%@",value]];
    };
}] subscribeNext:^(id x) {
    NSLog(@"%@",x);

}];

不正確之處,歡迎補充
測試代碼 https://github.com/CharType/ReactiveCocoaTest

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盯荤,一起剝皮案震驚了整個濱河市馋吗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌秋秤,老刑警劉巖宏粤,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脚翘,死亡現(xiàn)場離奇詭異,居然都是意外死亡绍哎,警方通過查閱死者的電腦和手機来农,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來崇堰,“玉大人备图,你說我怎么就攤上這事「习溃” “怎么了揽涮?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長饿肺。 經(jīng)常有香客問我蒋困,道長,這世上最難降的妖魔是什么敬辣? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任雪标,我火速辦了婚禮,結(jié)果婚禮上溉跃,老公的妹妹穿的比我還像新娘村刨。我一直安慰自己,他們只是感情好撰茎,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布嵌牺。 她就那樣靜靜地躺著,像睡著了一般龄糊。 火紅的嫁衣襯著肌膚如雪逆粹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天炫惩,我揣著相機與錄音僻弹,去河邊找鬼。 笑死他嚷,一個胖子當著我的面吹牛蹋绽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播筋蓖,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼卸耘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了扭勉?” 一聲冷哼從身側(cè)響起鹊奖,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涂炎,沒想到半個月后忠聚,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡唱捣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年两蟀,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片震缭。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡赂毯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拣宰,到底是詐尸還是另有隱情党涕,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布巡社,位于F島的核電站膛堤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏晌该。R本人自食惡果不足惜肥荔,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望朝群。 院中可真熱鬧燕耿,春花似錦、人聲如沸姜胖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽右莱。三九已至堵第,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間隧出,已是汗流浹背踏志。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留胀瞪,地道東北人针余。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像凄诞,于是被迫代替她去往敵國和親圆雁。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容