iOS-ReactiveCocoa學(xué)習(xí)筆記

關(guān)于ReactiveCocoa的比喻

“能夠把信號想象成水龍頭恭垦,僅僅只是里面不是水,而是玻璃球(value)晤斩,直徑跟水管的內(nèi)徑一樣,這樣就能保證玻璃球是依次排列姆坚。不會(huì)出現(xiàn)并排的情況(數(shù)據(jù)都是線性處理的澳泵,不會(huì)出現(xiàn)并發(fā)情況)。水龍頭的開關(guān)默認(rèn)是關(guān)的兼呵。除非有了接收方(subscriber)兔辅,才會(huì)打開。
這樣僅僅要有新的玻璃球進(jìn)來击喂,就會(huì)自己主動(dòng)傳送給接收方幢妄。
能夠在水龍頭上加一個(gè)過濾嘴(filter)。不符合的不讓通過茫负,也能夠加一個(gè)修改裝置,把球改變成符合自己的需求(map)乎赴。
也能夠把多個(gè)水龍頭合并成一個(gè)新的水龍頭(combineLatest:reduce:)忍法,這樣僅僅要當(dāng)中的一個(gè)水龍頭有玻璃球出來,這個(gè)新合并的水龍頭就會(huì)得到這個(gè)球榕吼。

1.RACSignal

#import <UIKit/UIKit.h>
@interface RACSignalController : UIViewController
@end
#import "RACSignalController.h"
@interface RACSignalController ()
@property (nonatomic, strong) id<RACSubscriber> subscriber;
@end

@implementation RACSignalController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.navigationItem.title = @"RACSignal";
    
    /**
     1. RACSiganl:信號類,一般表示將來有數(shù)據(jù)傳遞饿序,只要有數(shù)據(jù)改變,信號內(nèi)部接收到數(shù)據(jù)羹蚣,就會(huì)馬上發(fā)出數(shù)據(jù)原探。
     信號類(RACSiganl),只是表示當(dāng)數(shù)據(jù)改變時(shí)顽素,信號內(nèi)部會(huì)發(fā)出數(shù)據(jù)咽弦,它本身不具備發(fā)送信號的能力,而是交給內(nèi)部一個(gè)訂閱者去發(fā)出胁出。
     
     2. 默認(rèn)一個(gè)信號都是冷信號型型,也就是值改變了,也不會(huì)觸發(fā)全蝶,只有訂閱了這個(gè)信號闹蒜,這個(gè)信號才會(huì)變?yōu)闊嵝盘査峦鳎蹈淖兞瞬艜?huì)觸發(fā)。
     
     3. 如何訂閱信號:調(diào)用信號RACSignal的subscribeNext就能訂閱绷落。
     */
    
    
    
    // 核心:信號類
    // 信號類作用:只要有數(shù)據(jù)改變姥闪,就會(huì)把數(shù)據(jù)包裝成一個(gè)信號,傳遞出去砌烁。
    // 只要有數(shù)據(jù)改變筐喳,就會(huì)有信號發(fā)出。
    // 數(shù)據(jù)發(fā)出往弓,并不是信號類發(fā)出疏唾。
    
    // 1.創(chuàng)建信號 createSignal:didSubscribe(block)
    // RACDisposable:取消訂閱
    // RACSubscriber:發(fā)送數(shù)據(jù)
    
    // createSignal方法:
    // 1.創(chuàng)建RACDynamicSignal
    // 2.把didSubscribe保存到RACDynamicSignal
    
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // block調(diào)用時(shí)刻:當(dāng)信號被訂閱的時(shí)候就會(huì)調(diào)用
        // block作用:描述當(dāng)前信號哪些數(shù)據(jù)需要發(fā)送
        
        // 3.發(fā)送信號
        NSLog(@"調(diào)用了didSubscribe");
        // 通常:傳遞數(shù)據(jù)出去
        [subscriber sendNext:@"發(fā)送信號"];
        /**
         如果不在發(fā)送數(shù)據(jù),最好發(fā)送信號完成函似,內(nèi)部會(huì)自動(dòng)調(diào)用[RACDisposable disposable]取消訂閱信號
         */
        [subscriber sendCompleted];
        
        // 取消訂閱方法
        return [RACDisposable disposableWithBlock:^{
            // 信號什么時(shí)候被取消:1.自動(dòng)取消槐脏,當(dāng)一個(gè)信號的訂閱者被銷毀的時(shí)候,就會(huì)自動(dòng)取消訂閱 2.主動(dòng)取消
            // block調(diào)用時(shí)刻:一旦一個(gè)信號撇寞,被取消訂閱的時(shí)候就會(huì)調(diào)用
            // block作用:當(dāng)信號取消訂閱顿天,用于清空一些資源
            NSLog(@"信號銷毀了");
        }];
    }];
    
    // 2.訂閱信號
    [signal subscribeNext:^(id x) {
        NSLog(@"訂閱信號:%@",x);
    }];
    
    //輸出
    //訂閱信號:發(fā)送信號
    //信號銷毀了

    
    /**
     RACSignal底層實(shí)現(xiàn)
     1. 創(chuàng)建信號,首先把didSubscribe保存到信號中蔑担,還不會(huì)觸發(fā)牌废。
     2. 當(dāng)信號被訂閱,也就是調(diào)用signal的subscribeNext:nextBlock
     2.1 subscribeNext內(nèi)部會(huì)調(diào)用siganl的didSubscribe
     2.2 subscribeNext內(nèi)部會(huì)創(chuàng)建訂閱者subscriber啤握,并且把nextBlock保存到subscriber中
     3.0 siganl的didSubscribe中調(diào)用[subscriber sendNext:@"發(fā)送信號"];
     3.1 sendNext底層其實(shí)就是執(zhí)行subscriber的nextBlock
     */
    
    RACSignal *signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
       
        /**
         有一個(gè)全局變量保存值就不會(huì)走下面取消訂閱方法:現(xiàn)在有問題
         */
        self.subscriber = subscriber;
        
        [subscriber sendNext:@"123"];
        [subscriber sendCompleted];
        
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"走銷毀這個(gè)方法了");
        }];
    }];
    
    //訂閱信號返回RACDisposable
    RACDisposable *disposable = [signal1 subscribeNext:^(id x) {
        NSLog(@"接收打印的值:%@",x);
    }];
    
    // 默認(rèn)一個(gè)信號發(fā)送數(shù)據(jù)完畢們就會(huì)主動(dòng)取消訂閱.
    // 只要訂閱者在,就不會(huì)自動(dòng)取消信號訂閱
    // 手動(dòng)取消訂閱者
    [disposable dispose];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end

2.RACSubscriber

#import <UIKit/UIKit.h>

@interface RACSubscriberController : UIViewController

@end
#import "RACSubscriberController.h"
#import "RedView.h"

@interface RACSubscriberController ()

@end

@implementation RACSubscriberController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationItem.title = @"RACSubscriber";

    /**
     1. RACSubscriber: 表示訂閱者的意思鸟缕,用于發(fā)送信號,這是一個(gè)協(xié)議排抬,不是一個(gè)類懂从,只要遵守這個(gè)協(xié)議,并且實(shí)現(xiàn)方法才能成為訂閱者蹲蒲。通過create創(chuàng)建的信號番甩,都有一個(gè)訂閱者,幫助他發(fā)送數(shù)據(jù)届搁。
     
     2. RACDisposable:用于取消訂閱或者清理資源缘薛,當(dāng)信號發(fā)送完成或者發(fā)送錯(cuò)誤的時(shí)候,就會(huì)自動(dòng)觸發(fā)它卡睦。
     使用場景:不想監(jiān)聽某個(gè)信號時(shí)宴胧,可以通過它主動(dòng)取消訂閱信號。
     
     3. RACSubject:RACSubject:信號提供者表锻,自己可以充當(dāng)信號牺汤,又能發(fā)送信號。
     使用場景:通常用來代替代理浩嫌,有了它檐迟,就不必要定義代理了补胚。
     
     4. RACReplaySubject:重復(fù)提供信號類,RACSubject的子類追迟。
     
     RACReplaySubject與RACSubject區(qū)別:
     RACReplaySubject可以先發(fā)送信號溶其,在訂閱信號,RACSubject就不可以敦间。
     使用場景一:如果一個(gè)信號每被訂閱一次瓶逃,就需要把之前的值重復(fù)發(fā)送一遍,使用重復(fù)提供信號類廓块。
     使用場景二:可以設(shè)置capacity數(shù)量來限制緩存的value的數(shù)量,即只緩充最新的幾個(gè)值厢绝。
     
     */
    
    // RACSubject使用
    [self RACSubjectUse];
    
    // RACReplaySubject使用
    [self RACReplaySubjectUse];
    
    // RACSubject替換代理使用
    [self RACSubjectReplaceAgent];
    
}
#pragma mark - RACSubjectUse
- (void)RACSubjectUse {

    // RACSubject使用步驟
    // 1.創(chuàng)建信號 [RACSubject subject],跟RACSiganl不一樣带猴,創(chuàng)建信號時(shí)沒有block昔汉。
    // 2.訂閱信號 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
    // 3.發(fā)送信號 sendNext:(id)value
    
    // 1.創(chuàng)建信號
    RACSubject *subject = [RACSubject subject];
    // 2.訂閱信號
    [subject subscribeNext:^(id x) {
       //當(dāng)信號發(fā)出新值,就會(huì)調(diào)用.
        NSLog(@"第一個(gè)訂閱者%@",x);
    }];
    
    [subject subscribeNext:^(id x) {
        NSLog(@"第二個(gè)訂閱者%@",x);
    }];
    
    // 3.發(fā)送信號
    [subject sendNext:@"123456"];
    // 4.發(fā)送信號完成,內(nèi)部會(huì)自動(dòng)取消訂閱者
    [subject sendCompleted];
    
    // 輸出:
    // 第一個(gè)訂閱者123456
    // 第二個(gè)訂閱者123456

    
}
#pragma mark - RACReplaySubjectUse
-(void)RACReplaySubjectUse {

    // RACReplaySubject使用步驟:
    // 1.創(chuàng)建信號 [RACSubject subject]拴清,跟RACSiganl不一樣靶病,創(chuàng)建信號時(shí)沒有block。
    // 2.可以先訂閱信號口予,也可以先發(fā)送信號娄周。
    // 2.1 訂閱信號 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
    // 2.2 發(fā)送信號 sendNext:(id)value
    
    // RACReplaySubject:底層實(shí)現(xiàn)和RACSubject不一樣。
    // 1.調(diào)用sendNext發(fā)送信號沪停,把值保存起來煤辨,然后遍歷剛剛保存的所有訂閱者,一個(gè)一個(gè)調(diào)用訂閱者的nextBlock木张。
    // 2.調(diào)用subscribeNext訂閱信號掷酗,遍歷保存的所有值,一個(gè)一個(gè)調(diào)用訂閱者的nextBlock
    
    // 1.創(chuàng)建信號
    RACReplaySubject *replaySubject = [RACReplaySubject subject];
    // 2.發(fā)送信號
    [replaySubject sendNext:@"1"];
    [replaySubject sendNext:@"2"];
    // 3.訂閱信號
    [replaySubject subscribeNext:^(id x) {
        NSLog(@"第一個(gè)訂閱者接收到的數(shù)據(jù)%@",x);
    }];
    
    [replaySubject subscribeNext:^(id x) {

        NSLog(@"第二個(gè)訂閱者接收到的數(shù)據(jù)%@",x);
    }];
    
    // 輸出:
    // 第一個(gè)訂閱者接收到的數(shù)據(jù)1
    // 第一個(gè)訂閱者接收到的數(shù)據(jù)2
    // 第二個(gè)訂閱者接收到的數(shù)據(jù)1
    // 第二個(gè)訂閱者接收到的數(shù)據(jù)2
}

#pragma mark - RACSubject替換代理
- (void)RACSubjectReplaceAgent {

    RedView *redView = [[[NSBundle mainBundle] loadNibNamed:@"RedView" owner:nil options:nil] lastObject];
    redView.frame = CGRectMake(0, 64, 300, 200);
    redView.center = self.view.center;
    [redView.btnClickSignal subscribeNext:^(id x) {
        NSLog(@"---%@--",x);
    }];
    [self.view addSubview:redView];
}
@end

3.RAC集合

#import <UIKit/UIKit.h>

@interface RACSetViewController : UIViewController

@end
#import "RACSetViewController.h"
#import "Flag.h"

@interface RACSetViewController ()

@end

@implementation RACSetViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self tuple];
    [self array];
    [self dictionary];
    [self initWithDictionary];
}
#pragma mark- RACTuple數(shù)組
- (void)tuple {
   
    /**
     RACTuple:元組類,類似NSArray,用來包裝值.
     */
    //元組
    RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:@[@"123",@"345",@1]];
    NSString *first = tuple[0];
    NSLog(@"%@",first);
    
    //輸出:
    //123
}
#pragma mark- RACSequence數(shù)組
- (void)array {
    // 數(shù)組
    NSArray *arr = @[@"213",@"321",@1];
    
    //RAC 集合
    RACSequence *sequence = arr.rac_sequence;
    // 把集合轉(zhuǎn)換成信號
    RACSignal *signal = sequence.signal;
    //訂閱集合信號,內(nèi)部會(huì)自動(dòng)遍歷所有的元素發(fā)出來
    [signal subscribeNext:^(id x) {
        NSLog(@"遍歷數(shù)組%@",x);
    }];
    
    
    //輸出:
    /**
     遍歷數(shù)組213
     遍歷數(shù)組321
     遍歷數(shù)組1
     */
    
    
    //高級寫法
    [arr.rac_sequence.signal subscribeNext:^(id x) {
        NSLog(@"高級寫法遍歷數(shù)組打印%@",x);
    }];
    
    //輸出:
    /**
     高級寫法遍歷數(shù)組打印213
     高級寫法遍歷數(shù)組打印321
     高級寫法遍歷數(shù)組打印1
     */
}
#pragma mark- RACTuple數(shù)組
- (void)dictionary {
    // 字典
    NSDictionary *dict = @{@"sex":@"女",@"name":@"蒼老師",@"age":@18};
    
    //轉(zhuǎn)換成集合
    [dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
//        NSString *key = x[0];
//        NSString *value = x[1];
//        NSLog(@"%@ %@",key,value);
        // RACTupleUnpack:用來解析元組
        // 宏里面的參數(shù),傳需要解析出來的變量名
        //= 右邊,放需要解析的元組
        RACTupleUnpack(NSString *key, NSString *value) = x;
        NSLog(@"%@ %@",key,value);
     
    }];
    //輸出:
    /**
     sex 女
     name 蒼老師
     age 18
     */
}

#pragma mark- 字典轉(zhuǎn)模型
- (void)initWithDictionary {
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
    NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];
    
    //    NSMutableArray *arr = [NSMutableArray array];
    //    // rac_sequence注意點(diǎn):調(diào)用subscribeNext窟哺,并不會(huì)馬上執(zhí)行nextBlock,而是會(huì)等一會(huì)技肩。
    
    //    [dictArr.rac_sequence.signal subscribeNext:^(NSDictionary *x) {
    //        Flag *flag = [Flag flagWithDict:x];
    //        [arr addObject:flag];
    //    }];
    
    //高級用法
    // map:映射的意思且轨,目的:把原始值value映射成一個(gè)新值
    // array: 把集合轉(zhuǎn)換成數(shù)組
    // 底層實(shí)現(xiàn):當(dāng)信號被訂閱,會(huì)遍歷集合中的原始值虚婿,映射成新值旋奢,并且保存到新的數(shù)組里。
    NSArray *arr = [[dictArr.rac_sequence map:^id(NSDictionary *value) {
        return [Flag flagWithDict:value];
    }] array];
    
    NSLog(@"%@",arr);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end

4.RACMulticastConnection

#import <UIKit/UIKit.h>

@interface RACMulticastConnectionController : UIViewController

@end
#import "RACMulticastConnectionController.h"

@interface RACMulticastConnectionController ()

@end

@implementation RACMulticastConnectionController

- (void)viewDidLoad {
    [super viewDidLoad];

    /**
     RACMulticastConnection:用于當(dāng)一個(gè)信號然痊,被多次訂閱時(shí)至朗,為了保證創(chuàng)建信號時(shí),避免多次調(diào)用創(chuàng)建信號中的block剧浸,造成副作用锹引,可以使用這個(gè)類處理矗钟。
     使用注意:RACMulticastConnection通過RACSignal的-publish或者-muticast:方法創(chuàng)建.
     */
    
    
    [self RACMulticastConnection];
    
}

- (void)RACMulticastConnection {
    //RACMulticastConnection簡單使用:
    // RACMulticastConnection使用步驟:
    // 1.創(chuàng)建信號 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
    // 2.創(chuàng)建連接 RACMulticastConnection *connect = [signal publish];
    // 3.訂閱信號,注意:訂閱的不在是之前的信號,而是連接的信號嫌变。 [connect.signal subscribeNext:nextBlock]
    // 4.連接 [connect connect]
    
    // RACMulticastConnection底層原理:
    // 1.創(chuàng)建connect吨艇,connect.sourceSignal -> RACSignal(原始信號)  connect.signal -> RACSubject
    // 2.訂閱connect.signal,會(huì)調(diào)用RACSubject的subscribeNext腾啥,創(chuàng)建訂閱者东涡,而且把訂閱者保存起來,不會(huì)執(zhí)行block倘待。
    // 3.[connect connect]內(nèi)部會(huì)訂閱RACSignal(原始信號)疮跑,并且訂閱者是RACSubject
    // 3.1.訂閱原始信號,就會(huì)調(diào)用原始信號中的didSubscribe
    // 3.2 didSubscribe凸舵,拿到訂閱者調(diào)用sendNext祖娘,其實(shí)是調(diào)用RACSubject的sendNext
    // 4.RACSubject的sendNext,會(huì)遍歷RACSubject所有訂閱者發(fā)送信號。
    // 4.1 因?yàn)閯倓偟诙秸昙洌际窃谟嗛哛ACSubject贿条,因此會(huì)拿到第二步所有的訂閱者,調(diào)用他們的nextBlock
    
    // 需求:假設(shè)在一個(gè)信號中發(fā)送請求增热,每次訂閱一次都會(huì)發(fā)送請求整以,這樣就會(huì)導(dǎo)致多次請求。
    // 解決:使用RACMulticastConnection就能解決.
    
    // 1.創(chuàng)建信號
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSLog(@"發(fā)送請求");
        
        [subscriber sendNext:@"1"];
        return [RACDisposable disposableWithBlock:^{
            
        }];
    }];
    // 2.訂閱信號
    [signal subscribeNext:^(id x) {
       
        NSLog(@"接收數(shù)據(jù)");
    }];
    [signal subscribeNext:^(id x) {
        
        NSLog(@"接收數(shù)據(jù)");
    }];
    
    // 3.運(yùn)行結(jié)果峻仇,會(huì)執(zhí)行兩遍發(fā)送請求公黑,也就是每次訂閱都會(huì)發(fā)送一次請求
    
    
    
    
    
    // RACMulticastConnection:解決重復(fù)請求問題
    // 1.創(chuàng)建信號
    RACSignal *connectionSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSLog(@"發(fā)送請求--");
        [subscriber sendNext:@1];
        
        return [RACDisposable disposableWithBlock:^{
            
        }];
    }];
    
    //2.創(chuàng)建連接
    RACMulticastConnection *connect = [connectionSignal publish];
    
    //3.訂閱信號
    // 注意:訂閱信號伙窃,也不能激活信號荣德,只是保存訂閱者到數(shù)組,必須通過連接,當(dāng)調(diào)用連接耻蛇,就會(huì)一次性調(diào)用所有訂閱者的sendNext:
    [connect.signal subscribeNext:^(id x) {
        NSLog(@"訂閱者一信號--");
    }];
    [connect.signal subscribeNext:^(id x) {
        
        NSLog(@"訂閱者二信號--");
        
    }];
    //4.連接,激活信號
    [connect connect];
    
    //輸出:
    /**
     發(fā)送請求--
     訂閱者一信號--
     訂閱者二信號--
     */
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

5.RACCommand

#import <UIKit/UIKit.h>

@interface RACCommandController : UIViewController

@end
#import "RACCommandController.h"

@interface RACCommandController ()
@property (nonatomic, strong) RACCommand *command;
@end

@implementation RACCommandController

- (void)viewDidLoad {
    [super viewDidLoad];

    /**
     RACCommand:RAC中用于處理事件的類吭从,可以把事件如何處理,事件中的數(shù)據(jù)如何傳遞朝蜘,包裝到這個(gè)類中,他可以很方便的監(jiān)控事件的執(zhí)行過程涩金。
     
     使用場景:監(jiān)聽按鈕點(diǎn)擊谱醇,網(wǎng)絡(luò)請求
     */
    
    [self RACCommandUse];
    
    [self switchToLatest];
    
    [self commandData];
}

- (void)RACCommandUse {

    // 一、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è)信號全度,不能傳nil.
    // 2.如果不想要傳遞信號煮剧,直接創(chuàng)建空的信號[RACSignal empty];
    // 3.RACCommand中信號如果數(shù)據(jù)傳遞完,必須調(diào)用[subscriber sendCompleted],這時(shí)命令才會(huì)執(zhí)行完畢勉盅,否則永遠(yuǎn)處于執(zhí)行中佑颇。
    // 三、RACCommand設(shè)計(jì)思想:內(nèi)部signalBlock為什么要返回一個(gè)信號菇篡,這個(gè)信號有什么用漩符。
    // 1.在RAC開發(fā)中,通常會(huì)把網(wǎng)絡(luò)請求封裝到RACCommand驱还,直接執(zhí)行某個(gè)RACCommand就能發(fā)送請求嗜暴。
    // 2.當(dāng)RACCommand內(nèi)部請求到數(shù)據(jù)的時(shí)候,需要把請求的數(shù)據(jù)傳遞給外界议蟆,這時(shí)候就需要通過signalBlock返回的信號傳遞了闷沥。
    
    // 四、如何拿到RACCommand中返回信號發(fā)出的數(shù)據(jù)咐容。
    // 1.RACCommand有個(gè)執(zhí)行信號源executionSignals舆逃,這個(gè)是signal of signals(信號的信號),意思是信號發(fā)出的數(shù)據(jù)是信號,不是普通的類型戳粒。
    // 2.訂閱executionSignals就能拿到RACCommand中返回的信號路狮,然后訂閱signalBlock返回的信號,就能獲取發(fā)出的值蔚约。
    
    // 五奄妨、監(jiān)聽當(dāng)前命令是否正在執(zhí)行executing
    
    // 六、使用場景,監(jiān)聽按鈕點(diǎn)擊苹祟,網(wǎng)絡(luò)請求
    
    // 1.創(chuàng)建命令
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
       
        NSLog(@"執(zhí)行命令");
        // 創(chuàng)建空信號,必須返回信號
        // return [RACSignal empty];
        
        // 2.創(chuàng)建信號,用來傳遞數(shù)據(jù)
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
           
            [subscriber sendNext:@"請求數(shù)據(jù)"];
            
            // 注意:數(shù)據(jù)傳遞完砸抛,最好調(diào)用sendCompleted树枫,這時(shí)命令才執(zhí)行完畢直焙。
            [subscriber sendCompleted];
            
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"數(shù)據(jù)銷毀");
            }];
        }];
    }];
    
    // 強(qiáng)引用命令,不要被銷毀砂轻,否則接收不到數(shù)據(jù)
    _command = command;
    
    // 監(jiān)聽事件有沒有完成
    [command.executing subscribeNext:^(id x) {
        
        if ([x boolValue] == YES) { // 當(dāng)前正在執(zhí)行
            
            NSLog(@"當(dāng)前正在執(zhí)行");
        }else{
            
            // 執(zhí)行完成/沒有執(zhí)行
            NSLog(@"執(zhí)行完成/沒有執(zhí)行");
        }
    }];
    
    // 獲取信號中信號發(fā)送的最新信號
    [self.command.executionSignals.switchToLatest subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    
    // 執(zhí)行命令
    //[self.command execute:@1];
}


// RAC高級用法
- (void)switchToLatest {

    //創(chuàng)建信號中信號
    RACSubject *signalOfSignals = [RACSubject subject];
    RACSubject *signalA = [RACSubject subject];
    RACSubject *signalB = [RACSubject subject];
    // 訂閱信號
//        [signalOfSignals subscribeNext:^(RACSignal *x) {
//            [x subscribeNext:^(id x) {
//                NSLog(@"%@",x);
//            }];
//        }];
//     switchToLatest:獲取信號中信號發(fā)送的最新信號
    [signalOfSignals.switchToLatest subscribeNext:^(id x) {
        
        NSLog(@"%@",x);
    }];
    
    // 發(fā)送信號
    [signalOfSignals sendNext:signalA];
    
    [signalA sendNext:@1];
    [signalB sendNext:@"BB"];
    [signalA sendNext:@"11"];
}
- (void)commandData
{
    // RACCommand:處理事件
    // RACCommand:不能返回一個(gè)空的信號
    // 1.創(chuàng)建命令
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        // input:執(zhí)行命令傳入?yún)?shù)
        // Block調(diào)用:執(zhí)行命令的時(shí)候就會(huì)調(diào)用
        NSLog(@"input-%@",input);
        
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            
            // 發(fā)送數(shù)據(jù)
            [subscriber sendNext:@"執(zhí)行命令產(chǎn)生的數(shù)據(jù)"];
            
            return nil;
        }];
    }];
    
    // 如何拿到執(zhí)行命令中產(chǎn)生的數(shù)據(jù)
    // 訂閱命令內(nèi)部的信號
    // 1.方式一:直接訂閱執(zhí)行命令返回的信號
    // 2.方式二:
    
    // 2.執(zhí)行命令
    RACSignal *signal = [command execute:@1];
    
    // 3.訂閱信號
    [signal subscribeNext:^(id x) {
        NSLog(@"x-%@",x);
    }];
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end

6.RAC常用方法--常見宏

@interface RACMethodUseViewController : UIViewController

@end
#import "RACMethodUseViewController.h"
#import "RedView.h"

@interface RACMethodUseViewController ()

@end

@implementation RACMethodUseViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    #pragma mark -常見的五個(gè)宏
    ////常見的五個(gè)宏
    //使用宏定義要單獨(dú)導(dǎo)入 #import <RACEXTScope.h>

    // 1:
    /*
     RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于給某個(gè)對象的某個(gè)屬性綁定信號
     只要文本框文字改變奔誓,就會(huì)修改label的文字
     RAC(self.labelView,text) = _textField.rac_textSignal;
     */
    
    // 2:
    /*
    RACObserve(self, name):監(jiān)聽某個(gè)對象的某個(gè)屬性,返回的是信號。
    [RACObserve(self.view, center) subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    */
    
    
    //當(dāng)RACObserve放在block里面使用時(shí)一定要加上weakify搔涝,不管里面有沒有使用到self厨喂;否則會(huì)內(nèi)存泄漏,因?yàn)镽ACObserve宏里面就有一個(gè)self
    /*
    @weakify(self);
    RACSignal *signal3 = [anotherSignal flattenMap:^(NSArrayController *arrayController) {
     Avoids a retain cycle because of RACObserve implicitly referencing self
        @strongify(self);
        return RACObserve(arrayController, items);
    }];
     */
    
    //3:
    //@weakify(Obj)和@strongify(Obj),一般兩個(gè)都是配套使用,在主頭文件(ReactiveCocoa.h)中并沒有導(dǎo)入体谒,需要自己手動(dòng)導(dǎo)入,RACEXTScope.h才可以使用臼婆。但是每次導(dǎo)入都非常麻煩抒痒,只需要在主頭文件自己導(dǎo)入就好了
    
    // 4:
    /*
    RACTuplePack:把數(shù)據(jù)包裝成RACTuple(元組類)
    把參數(shù)中的數(shù)據(jù)包裝成元組
    RACTuple *tuple = RACTuplePack(@10,@20);
     */
    
    //5:
    /*
    RACTupleUnpack:把RACTuple(元組類)解包成對應(yīng)的數(shù)據(jù)
    把參數(shù)中的數(shù)據(jù)包裝成元組
    RACTuple *tuple = RACTuplePack(@"xmg",@20);
    
    解包元組,會(huì)把元組的值颁褂,按順序給參數(shù)里面的變量賦值
    name = @"xmg" age = @20
    RACTupleUnpack(NSString *name,NSNumber *age) = tuple;
    */
    
    
    [self initView];
}

- (void)initView {

    self.navigationItem.title = @"RAC常用方法";
    self.view.backgroundColor = [UIColor whiteColor];
    
#pragma mark -監(jiān)聽按鈕的點(diǎn)擊事件
    // 1.監(jiān)聽按鈕的點(diǎn)擊事件
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.frame = CGRectMake(100, 100, 80, 40);
    [button setTitle:@"點(diǎn)擊事件" forState:UIControlStateNormal];
    [button setBackgroundColor:[UIColor redColor]];
    [self.view addSubview:button];
    [[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
        NSLog(@"按鈕被點(diǎn)擊了");
    }];

#pragma mark -代理的使用
    // 2.代理的使用
    // 2.1 只要傳值,就必須使用RACSubject
    RedView *redView = [[[NSBundle mainBundle] loadNibNamed:@"RedView" owner:nil options:nil] lastObject];
    redView.frame = CGRectMake(0, 140, self.view.bounds.size.width, 200);
    [self.view addSubview:redView];
    [redView.btnClickSignal subscribeNext:^(id x) {
        NSLog(@"---點(diǎn)擊按鈕了,觸發(fā)了事件");
    }];
    
    // 2.2把控制器調(diào)用didReceiveMemoryWarning轉(zhuǎn)換成信號
    // rac_signalForSelector:監(jiān)聽某對象有沒有調(diào)用某方法
    [[self rac_signalForSelector:@selector(didReceiveMemoryWarning)] subscribeNext:^(id x) {
        NSLog(@"控制器調(diào)用didReceiveMemoryWarning");
    }];
    
#pragma mark -KVO
    // 3.KVO
    // 把監(jiān)聽redV的center屬性改變轉(zhuǎn)換成信號故响,只要值改變就會(huì)發(fā)送信號
    // observer:可以傳入nil
    [[redView rac_valuesAndChangesForKeyPath:@"center" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {
        NSLog(@"center:%@",x);
    }];
    
#pragma mark -代替通知
    // 4.代替通知
    UITextField *textfield = [[UITextField alloc] initWithFrame:CGRectMake(100, 340, 150, 40)];
    textfield.placeholder = @"監(jiān)聽鍵盤的彈起";
    textfield.borderStyle = UITextBorderStyleRoundedRect;
    [self.view addSubview:textfield];
    
    
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {
        NSLog(@"監(jiān)聽鍵盤的高度:%@",x);
    }];
    
#pragma mark -監(jiān)聽文本框的文字改變
    // 5.監(jiān)聽文本框的文字改變
    [textfield.rac_textSignal subscribeNext:^(id x) {
        NSLog(@"輸入框中文字改變了---%@",x);
    }];
    
#pragma mark -處理多個(gè)請求傀广,都返回結(jié)果的時(shí)候,統(tǒng)一做處理.
    // 6.處理多個(gè)請求彩届,都返回結(jié)果的時(shí)候伪冰,統(tǒng)一做處理.
    RACSignal *request1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        // 發(fā)送請求1
        [subscriber sendNext:@"發(fā)送請求1"];
        return nil;
    }];
    
    RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // 發(fā)送請求2
        [subscriber sendNext:@"發(fā)送請求2"];
        return nil;
    }];
    
    // 使用注意:幾個(gè)信號,參數(shù)一的方法就幾個(gè)參數(shù)樟蠕,每個(gè)參數(shù)對應(yīng)信號發(fā)出的數(shù)據(jù)贮聂。
    [self rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalsFromArray:@[request1,request2]];
}

// 更新UI
- (void)updateUIWithR1:(id)data r2:(id)data1
{
    NSLog(@"更新UI%@  %@",data,data1);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end

7.RAC操作方法一

#import <UIKit/UIKit.h>

@interface RACOperationMethodController : UIViewController

@end
//1.ReactiveCocoa常見操作方法介紹。

/**
 1.1 ReactiveCocoa操作須知
 
 所有的信號(RACSignal)都可以進(jìn)行操作處理寨辩,因?yàn)樗胁僮鞣椒ǘ级x在RACStream.h中吓懈,而RACSignal繼承RACStream。
 1.2 ReactiveCocoa操作思想
 
 運(yùn)用的是Hook(鉤子)思想靡狞,Hook是一種用于改變API(應(yīng)用程序編程接口:方法)執(zhí)行結(jié)果的技術(shù).
 Hook用處:截獲API調(diào)用的技術(shù)耻警。
 Hook原理:在每次調(diào)用一個(gè)API返回結(jié)果之前,先執(zhí)行你自己的方法甸怕,改變結(jié)果的輸出甘穿。
 RAC開發(fā)方式:RAC中核心開發(fā)方式,也是綁定梢杭,之前的開發(fā)方式是賦值温兼,而用RAC開發(fā),應(yīng)該把重心放在綁定式曲,也就是可以在創(chuàng)建一個(gè)對象的時(shí)候妨托,就綁定好以后想要做的事情,而不是等賦值之后在去做事情吝羞。
 列如:把數(shù)據(jù)展示到控件上兰伤,之前都是重寫控件的setModel方法,用RAC就可以在一開始創(chuàng)建控件的時(shí)候钧排,就綁定好數(shù)據(jù)敦腔。
 1.3 ReactiveCocoa核心方法bind
 
 ReactiveCocoa操作的核心方法是bind(綁定),給RAC中的信號進(jìn)行綁定,只要信號一發(fā)送數(shù)據(jù)恨溜,就能監(jiān)聽到符衔,從而把發(fā)送數(shù)據(jù)改成自己想要的數(shù)據(jù)。
 
 在開發(fā)中很少使用bind方法糟袁,bind屬于RAC中的底層方法判族,RAC已經(jīng)封裝了很多好用的其他方法,底層都是調(diào)用bind项戴,用法比bind簡單.
 
 bind方法簡單介紹和使用形帮。
 */

/**
 // 假設(shè)想監(jiān)聽文本框的內(nèi)容,并且在每次輸出結(jié)果的時(shí)候,都在文本框的內(nèi)容拼接一段文字“輸出:”
 
 // 方式一:在返回結(jié)果后辩撑,拼接界斜。
 [_textField.rac_textSignal subscribeNext:^(id x) {
 
 NSLog(@"輸出:%@",x);
 
 }];
 
 // 方式二:在返回結(jié)果前,拼接合冀,使用RAC中bind方法做處理各薇。
 // bind方法參數(shù):需要傳入一個(gè)返回值是RACStreamBindBlock的block參數(shù)
 // RACStreamBindBlock是一個(gè)block的類型,返回值是信號君躺,參數(shù)(value,stop)峭判,因此參數(shù)的block返回值也是一個(gè)block。
 
 // RACStreamBindBlock:
 // 參數(shù)一(value):表示接收到信號的原始值晰洒,還沒做處理
 // 參數(shù)二(*stop):用來控制綁定Block朝抖,如果*stop = yes,那么就會(huì)結(jié)束綁定。
 // 返回值:信號谍珊,做好處理治宣,在通過這個(gè)信號返回出去,一般使用RACReturnSignal,需要手動(dòng)導(dǎo)入頭文件RACReturnSignal.h砌滞。
 
 // bind方法使用步驟:
 // 1.傳入一個(gè)返回值RACStreamBindBlock的block侮邀。
 // 2.描述一個(gè)RACStreamBindBlock類型的bindBlock作為block的返回值。
 // 3.描述一個(gè)返回結(jié)果的信號贝润,作為bindBlock的返回值绊茧。
 // 注意:在bindBlock中做信號結(jié)果的處理。
 
 // 底層實(shí)現(xiàn):
 // 1.源信號調(diào)用bind,會(huì)重新創(chuàng)建一個(gè)綁定信號打掘。
 // 2.當(dāng)綁定信號被訂閱华畏,就會(huì)調(diào)用綁定信號中的didSubscribe,生成一個(gè)bindingBlock尊蚁。
 // 3.當(dāng)源信號有內(nèi)容發(fā)出亡笑,就會(huì)把內(nèi)容傳遞到bindingBlock處理,調(diào)用bindingBlock(value,stop)
 // 4.調(diào)用bindingBlock(value,stop)横朋,會(huì)返回一個(gè)內(nèi)容處理完成的信號(RACReturnSignal)仑乌。
 // 5.訂閱RACReturnSignal,就會(huì)拿到綁定信號的訂閱者琴锭,把處理完成的信號內(nèi)容發(fā)送出來晰甚。
 
 // 注意:不同訂閱者,保存不同的nextBlock决帖,看源碼的時(shí)候厕九,一定要看清楚訂閱者是哪個(gè)。
 // 這里需要手動(dòng)導(dǎo)入#import <ReactiveCocoa/RACReturnSignal.h>地回,才能使用RACReturnSignal扁远。
 
 [[_textField.rac_textSignal bind:^RACStreamBindBlock{
 
 // 什么時(shí)候調(diào)用:
 // block作用:表示綁定了一個(gè)信號.
 
 return ^RACStream *(id value, BOOL *stop){
 
 // 什么時(shí)候調(diào)用block:當(dāng)信號有新的值發(fā)出腺阳,就會(huì)來到這個(gè)block。
 
 // block作用:做返回值的處理
 
 // 做好處理穿香,通過信號返回出去.
 return [RACReturnSignal return:[NSString stringWithFormat:@"輸出:%@",value]];
 };
 
 }] subscribeNext:^(id x) {
 
 NSLog(@"%@",x);
 
 }];
 */
#import "RACOperationMethodController.h"
#import <RACReturnSignal.h>
@interface RACOperationMethodController ()
@property (strong, nonatomic) UITextField *textField;

@end

@implementation RACOperationMethodController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self initView];
    [self flattenMap];
    [self map];
    [self flatternMapWithMap];
    [self RACOperationMethodCombination];
}

- (void)initView {
    self.navigationItem.title = @"RAC 操作方法一";
    self.view.backgroundColor = [UIColor whiteColor];
    
    _textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 150, 40)];
    _textField.center = self.view.center;
    _textField.placeholder = @"請輸入內(nèi)容";
    [self.view addSubview:_textField];
}

//flattenMap簡單使用
// 監(jiān)聽文本框的內(nèi)容改變,把結(jié)構(gòu)重新映射成一個(gè)新值.
// flattenMap作用:把源信號的內(nèi)容映射成一個(gè)新的信號绎速,信號可以是任意類型皮获。
- (void)flattenMap {
    
    // flattenMap使用步驟:
    // 1.傳入一個(gè)block,block類型是返回值RACStream纹冤,參數(shù)value
    // 2.參數(shù)value就是源信號的內(nèi)容洒宝,拿到源信號的內(nèi)容做處理
    // 3.包裝成RACReturnSignal信號,返回出去萌京。
    
    // flattenMap底層實(shí)現(xiàn):
    // 0.flattenMap內(nèi)部調(diào)用bind方法實(shí)現(xiàn)的,flattenMap中block的返回值雁歌,會(huì)作為bind中bindBlock的返回值。
    // 1.當(dāng)訂閱綁定信號知残,就會(huì)生成bindBlock靠瞎。
    // 2.當(dāng)源信號發(fā)送內(nèi)容,就會(huì)調(diào)用bindBlock(value, *stop)
    // 3.調(diào)用bindBlock求妹,內(nèi)部就會(huì)調(diào)用flattenMap的block乏盐,flattenMap的block作用:就是把處理好的數(shù)據(jù)包裝成信號。
    // 4.返回的信號最終會(huì)作為bindBlock中的返回信號制恍,當(dāng)做bindBlock的返回信號父能。
    // 5.訂閱bindBlock的返回信號,就會(huì)拿到綁定信號的訂閱者净神,把處理完成的信號內(nèi)容發(fā)送出來何吝。

    [[_textField.rac_textSignal flattenMap:^RACStream *(id value) {
        // block什么時(shí)候 : 源信號發(fā)出的時(shí)候,就會(huì)調(diào)用這個(gè)block鹃唯。
        
        // block作用 : 改變源信號的內(nèi)容爱榕。
        // 返回值:綁定信號的內(nèi)容.
        return [RACReturnSignal return:[NSString stringWithFormat:@"輸出:%@",value]];
    }] subscribeNext:^(id x) {
         // 訂閱綁定信號,每當(dāng)源信號發(fā)送內(nèi)容俯渤,做完處理呆细,就會(huì)調(diào)用這個(gè)block。
        NSLog(@"輸出:flattenMap%@",x);
    }];
}

- (void)map {

    //Map簡單使用:
    // 監(jiān)聽文本框的內(nèi)容改變八匠,把結(jié)構(gòu)重新映射成一個(gè)新值.
    // Map作用:把源信號的值映射成一個(gè)新的值
    
    // Map使用步驟:
    // 1.傳入一個(gè)block,類型是返回對象絮爷,參數(shù)是value
    // 2.value就是源信號的內(nèi)容,直接拿到源信號的內(nèi)容做處理
    // 3.把處理好的內(nèi)容梨树,直接返回就好了坑夯,不用包裝成信號,返回的值抡四,就是映射的值柜蜈。
    
    // Map底層實(shí)現(xiàn):
    // 0.Map底層其實(shí)是調(diào)用flatternMap,Map中block中的返回的值會(huì)作為flatternMap中block中的值仗谆。
    // 1.當(dāng)訂閱綁定信號,就會(huì)生成bindBlock淑履。
    // 3.當(dāng)源信號發(fā)送內(nèi)容隶垮,就會(huì)調(diào)用bindBlock(value, *stop)
    // 4.調(diào)用bindBlock,內(nèi)部就會(huì)調(diào)用flattenMap的block
    // 5.flattenMap的block內(nèi)部會(huì)調(diào)用Map中的block秘噪,把Map中的block返回的內(nèi)容包裝成返回的信號狸吞。
    // 5.返回的信號最終會(huì)作為bindBlock中的返回信號,當(dāng)做bindBlock的返回信號指煎。
    // 6.訂閱bindBlock的返回信號蹋偏,就會(huì)拿到綁定信號的訂閱者,把處理完成的信號內(nèi)容發(fā)送出來至壤。
    
    [[_textField.rac_textSignal map:^id(id value) {
        // 當(dāng)源信號發(fā)出威始,就會(huì)調(diào)用這個(gè)block,修改源信號的內(nèi)容
        // 返回值:就是處理完源信號的內(nèi)容像街。
        return [NSString stringWithFormat:@"輸出:%@",value];
    }] subscribeNext:^(id x) {
        
        NSLog(@"map輸出%@",x);
    }];
}

- (void)flatternMapWithMap {

    /**
     FlatternMap和Map的區(qū)別
     
     1.FlatternMap中的Block返回信號黎棠。
     2.Map中的Block返回對象。
     3.開發(fā)中镰绎,如果信號發(fā)出的值不是信號葫掉,映射一般使用Map
     4.開發(fā)中,如果信號發(fā)出的值是信號跟狱,映射一般使用FlatternMap俭厚。
     總結(jié):signalOfsignals用FlatternMap。
     */
    
    //創(chuàng)建信號中的信號
    RACSubject *signalOfsignals = [RACSubject subject];
    RACSubject *signal = [RACSubject subject];
    
    [[signalOfsignals flattenMap:^RACStream *(id value) {
       
        //當(dāng)signalOfsignals的signals發(fā)出信號才會(huì)調(diào)用
        return value;
    }] subscribeNext:^(id x) {
        // 只有signalOfsignals的signal發(fā)出信號才會(huì)調(diào)用驶臊,因?yàn)閮?nèi)部訂閱了bindBlock中返回的信號挪挤,也就是flattenMap返回的信號。
        // 也就是flattenMap返回的信號發(fā)出內(nèi)容关翎,才會(huì)調(diào)用扛门。
        
        NSLog(@"%@aaa",x);
    }];
    
    // 信號的信號發(fā)送信號
    [signalOfsignals sendNext:signal];
    
    // 信號發(fā)送內(nèi)容
    [signal sendNext:@1];
}

//RAC 操作之組合
- (void)RACOperationMethodCombination {

    //一.concat:按一定順序拼接信號,當(dāng)多個(gè)信號發(fā)出的時(shí)候纵寝,有順序的接收信號论寨。
    RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@1];
        [subscriber sendCompleted];
        return nil;
    }];
    
    RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@2];
        [subscriber sendCompleted];
        
        return nil;
    }];
    
    // 把signalA拼接到signalB后,signalA發(fā)送完成爽茴,signalB才會(huì)被激活葬凳。
    RACSignal *concatSignal = [signalA concat:signalB];
    
    // 以后只需要面對拼接信號開發(fā)。
    // 訂閱拼接的信號室奏,不需要單獨(dú)訂閱signalA火焰,signalB
    // 內(nèi)部會(huì)自動(dòng)訂閱。
    // 注意:第一個(gè)信號必須發(fā)送完成胧沫,第二個(gè)信號才會(huì)被激活
    [concatSignal subscribeNext:^(id x) {
        
        NSLog(@"%@",x);
    }];
    
    // concat底層實(shí)現(xiàn):
    // 1.當(dāng)拼接信號被訂閱昌简,就會(huì)調(diào)用拼接信號的didSubscribe
    // 2.didSubscribe中占业,會(huì)先訂閱第一個(gè)源信號(signalA)
    // 3.會(huì)執(zhí)行第一個(gè)源信號(signalA)的didSubscribe
    // 4.第一個(gè)源信號(signalA)didSubscribe中發(fā)送值,就會(huì)調(diào)用第一個(gè)源信號(signalA)訂閱者的nextBlock,通過拼接信號的訂閱者把值發(fā)送出來.
    // 5.第一個(gè)源信號(signalA)didSubscribe中發(fā)送完成纯赎,就會(huì)調(diào)用第一個(gè)源信號(signalA)訂閱者的completedBlock,訂閱第二個(gè)源信號(signalB)這時(shí)候才激活(signalB)谦疾。
    // 6.訂閱第二個(gè)源信號(signalB),執(zhí)行第二個(gè)源信號(signalB)的didSubscribe
    // 7.第二個(gè)源信號(signalA)didSubscribe中發(fā)送值,就會(huì)通過拼接信號的訂閱者把值發(fā)送出來.
    
    //二.then:用于連接兩個(gè)信號,當(dāng)?shù)谝粋€(gè)信號完成犬金,才會(huì)連接then返回的信號餐蔬。
    // then:用于連接兩個(gè)信號,當(dāng)?shù)谝粋€(gè)信號完成佑附,才會(huì)連接then返回的信號
    // 注意使用then,之前信號的值會(huì)被忽略掉.
    // 底層實(shí)現(xiàn):1仗考、先過濾掉之前的信號發(fā)出的值音同。2.使用concat連接then返回的信號
    
    [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        [subscriber sendNext:@1];
        [subscriber sendCompleted];
        
        return nil;
        
    }] then:^RACSignal *{
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@2];
            return nil;
        }];
    }] subscribeNext:^(id x) {
        // 只能接收到第二個(gè)信號的值,也就是then返回信號的值
        NSLog(@"%@",x);
    }];
    
    
    //三.merge:把多個(gè)信號合并為一個(gè)信號,任何一個(gè)信號有新值的時(shí)候就會(huì)調(diào)用.
    //merge: 把多個(gè)信號合并成一個(gè)信號
    //創(chuàng)建多個(gè)信號
    
    RACSignal *signalC = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        [subscriber sendNext:@3];
        
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"信號釋放了");
        }];
    }];
    
    RACSignal *signalD = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@4];
        
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"信號釋放了");
        }];
    }];
    
    //合并信號.任何一個(gè)信號發(fā)送數(shù)據(jù),都能監(jiān)聽到
    
    RACSignal *mergeSignal = [signalC merge:signalD];
    [mergeSignal subscribeNext:^(id x) {
        NSLog(@"合并信號,任何一個(gè)數(shù)據(jù)發(fā)送數(shù)據(jù),都能監(jiān)聽到%@",x);
    }];
    
    // 底層實(shí)現(xiàn):
    // 1.合并信號被訂閱的時(shí)候秃嗜,就會(huì)遍歷所有信號权均,并且發(fā)出這些信號。
    // 2.每發(fā)出一個(gè)信號锅锨,這個(gè)信號就會(huì)被訂閱
    // 3.也就是合并信號一被訂閱叽赊,就會(huì)訂閱里面所有的信號。
    // 4.只要有一個(gè)信號被發(fā)出就會(huì)被監(jiān)聽必搞。
    
    //四.zipWith:把兩個(gè)信號壓縮成一個(gè)信號必指,只有當(dāng)兩個(gè)信號同時(shí)發(fā)出信號內(nèi)容時(shí),并且把兩個(gè)信號的內(nèi)容合并成一個(gè)元組恕洲,才會(huì)觸發(fā)壓縮流的next事件塔橡。
    
    RACSignal *signalE = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@5];
        
        return [RACDisposable disposableWithBlock:^{
            
        }];
    }];
    
    RACSignal *signalF = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@6];
        
        return [RACDisposable disposableWithBlock:^{
            
        }];
    }];
    
    // 壓縮信號A,信號B
    
    RACSignal *zipSignal = [signalE zipWith:signalF];
    
    [zipSignal subscribeNext:^(id x) {
        NSLog(@"壓縮的信號%@",x);
    }];
    
    // 底層實(shí)現(xiàn):
    // 1.定義壓縮信號霜第,內(nèi)部就會(huì)自動(dòng)訂閱signalA葛家,signalB
    // 2.每當(dāng)signalA或者signalB發(fā)出信號,就會(huì)判斷signalA泌类,signalB有沒有發(fā)出個(gè)信號癞谒,有就會(huì)把最近發(fā)出的信號都包裝成元組發(fā)出。
    
    
    //第五:combineLatest:將多個(gè)信號合并起來刃榨,并且拿到各個(gè)信號的最新的值,必須每個(gè)合并的signal至少都有過一次sendNext弹砚,才會(huì)觸發(fā)合并的信號.
    
    RACSignal *signalG = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@5];
        
        return [RACDisposable disposableWithBlock:^{
            
        }];
    }];
    
    RACSignal *signalH = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@6];
        
        return [RACDisposable disposableWithBlock:^{
            
        }];
    }];
    
    // 把兩個(gè)信號組合成一個(gè)信號,跟zip一樣,沒什么區(qū)別
    RACSignal *combineSignal = [signalG combineLatestWith:signalH];
    
    [combineSignal subscribeNext:^(id x) {
       NSLog(@"把兩個(gè)信號組合成一個(gè)信號%@",x);
    }];
    
    // 底層實(shí)現(xiàn):
    // 1.當(dāng)組合信號被訂閱枢希,內(nèi)部會(huì)自動(dòng)訂閱signalA迅栅,signalB,必須兩個(gè)信號都發(fā)出內(nèi)容,才會(huì)被觸發(fā)晴玖。
    // 2.并且把兩個(gè)信號組合成元組發(fā)出读存。
    
    
    //第六:reduce聚合:用于信號發(fā)出的內(nèi)容是元組为流,把信號發(fā)出元組的值聚合成一個(gè)值
    RACSignal *signalL = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@5];
        
        return [RACDisposable disposableWithBlock:^{
            
        }];
    }];
    
    RACSignal *signalM = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@6];
        
        return [RACDisposable disposableWithBlock:^{
            
        }];
    }];
    
    RACSignal *reduceSignal = [RACSignal combineLatest:@[signalL,signalM] reduce:^id(NSNumber *num1, NSNumber *num2){
        return [NSString stringWithFormat:@"reduce聚合%@ %@",num1,num2];
    }];
    
    [reduceSignal subscribeNext:^(id x) {
        NSLog(@"%@",x);
 
    }];
    
    // // 底層實(shí)現(xiàn):
    // 1.訂閱聚合信號,每次有內(nèi)容發(fā)出让簿,就會(huì)執(zhí)行reduceblcok敬察,把信號內(nèi)容轉(zhuǎn)換成reduceblcok返回的值。
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    [self.view endEditing:YES];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

8.RAC操作方法二

#import <UIKit/UIKit.h>

@interface RACOperationMethodFilteringVC : UIViewController

@end
#import "RACOperationMethodFilteringVC.h"

@interface RACOperationMethodFilteringVC ()
@property (strong, nonatomic) UITextField *textField;

@end

@implementation RACOperationMethodFilteringVC

- (void)viewDidLoad {
    [super viewDidLoad];

    [self initView];
    [self filter];
    [self ignore];
    [self ignoreValues];
    [self takeUntilBlock];
    [self distinctUntilChanged];
    [self take];
    [self takeLast];
    [self takeUntil];
    [self skip];
    [self switchToLatest];
}

- (void)initView {

    self.navigationItem.title = @"RAC操作方法二";
    self.view.backgroundColor = [UIColor whiteColor];
    
    _textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 150, 40)];
    _textField.center = self.view.center;
    _textField.borderStyle = UITextBorderStyleBezel;
    _textField.placeholder = @"請輸入內(nèi)容";
    [self.view addSubview:_textField];
}

//過濾信號尔当,使用它可以獲取滿足條件的信號.
- (void)filter {

    //filter 過濾
    //每次信號發(fā)出,會(huì)先執(zhí)行過濾條件判斷
    [_textField.rac_textSignal filter:^BOOL(NSString *value) {
       
        return value.length > 3;
    }];
}

//ignore:忽略完某些值的信號.
- (void)ignore {
    // 內(nèi)部調(diào)用filter過濾莲祸,忽略掉ignore的值
    [[_textField.rac_textSignal ignore:@"1"] subscribeNext:^(id x) {
        NSLog(@"ignore%@",x);
    }];
    
}

//ignoreValues 這個(gè)比較極端,忽略所有值椭迎,只關(guān)心Signal結(jié)束锐帜,也就是只取Comletion和Error兩個(gè)消息,中間所有值都丟棄
- (void)ignoreValues {
    
    RACSignal *signal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"1"];
        [subscriber sendNext:@"3"];
        [subscriber sendNext:@"15"];
        [subscriber sendNext:@"wujy"];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"執(zhí)行清理");
        }];
    }];
    
    
    [[signal ignoreValues] subscribeNext:^(id x) {
        //它是沒機(jī)會(huì)執(zhí)行  因?yàn)閕gnoreValues已經(jīng)忽略所有的next值
        NSLog(@"ignoreValues當(dāng)前值:%@",x);
    } error:^(NSError *error) {
        NSLog(@"ignoreValues error");
    } completed:^{
        NSLog(@"ignoreValues completed");
    }];
    
}


//distinctUntilChanged:當(dāng)上一次的值和當(dāng)前的值有明顯的變化就會(huì)發(fā)出信號畜号,否則會(huì)被忽略掉缴阎。
- (void)distinctUntilChanged {

    // 過濾,當(dāng)上一次和當(dāng)前的值不一樣简软,就會(huì)發(fā)出內(nèi)容蛮拔。
    // 在開發(fā)中,刷新UI經(jīng)常使用痹升,只有兩次數(shù)據(jù)不一樣才需要刷新
    
    [[_textField.rac_textSignal distinctUntilChanged] subscribeNext:^(id x) {
        NSLog(@"distinctUntilChanged%@",x);
    }];
}

//take:從開始一共取N次的信號
- (void)take {

    //1.創(chuàng)建信號
    RACSubject *signal = [RACSubject subject];
    //2.處理信號,訂閱信號
    [[signal take:1] subscribeNext:^(id x) {
        NSLog(@"take:%@",x);

    }];
    
    // 3.發(fā)送信號
    [signal sendNext:@1];
    
    [signal sendNext:@2];
}

//takeLast:取最后N次的信號,前提條件建炫,訂閱者必須調(diào)用完成,因?yàn)橹挥型瓿商鄱辏椭揽偣灿卸嗌傩盘?
- (void)takeLast {

    //1.創(chuàng)建信號
    RACSubject *signal = [RACSubject subject];
    //2.處理信號,訂閱信號
    [[signal takeLast:2] subscribeNext:^(id x) {
        NSLog(@"%@",x);

    }];
    
    //3.發(fā)送信號
    [signal sendNext:@1];
    [signal sendNext:@332];
    [signal sendNext:@333];
    [signal sendCompleted];
}

//takeUntil:(RACSignal *):獲取信號直到執(zhí)行完這個(gè)信號
- (void)takeUntil {
    // 監(jiān)聽文本框的改變肛跌,知道當(dāng)前對象被銷毀
    [_textField.rac_textSignal takeUntil:self.rac_willDeallocSignal];
}

//takeUntilBlock 對于每個(gè)next值,運(yùn)行block察郁,當(dāng)block返回YES時(shí)停止取值
- (void)takeUntilBlock {
    
    RACSignal *signal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"1"];
        [subscriber sendNext:@"3"];
        [subscriber sendNext:@"15"];
        [subscriber sendNext:@"wujy"];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"執(zhí)行清理");
        }];
    }];
    
    [[signal takeUntilBlock:^BOOL(NSString *x) {
        if ([x isEqualToString:@"15"]) {
            return YES;
        }
        return NO;
    }] subscribeNext:^(id x) {
        NSLog(@"takeUntilBlock 獲取的值:%@",x);
    }];
    
    //    輸出
    //    takeUntilBlock 獲取的值:1
    //    takeUntilBlock 獲取的值:3
}


//skip:(NSUInteger):跳過幾個(gè)信號,不接受惋砂。
- (void)skip {

    [[_textField.rac_textSignal skip:1] subscribeNext:^(id x) {
        NSLog(@"跳過幾個(gè)信號不接收%@",x);
    }];
}

//switchToLatest:用于signalOfSignals(信號的信號),有時(shí)候信號也會(huì)發(fā)出信號绳锅,會(huì)在signalOfSignals中西饵,獲取signalOfSignals發(fā)送的最新信號。
- (void)switchToLatest {
    RACSubject *signalOfSignals = [RACSubject subject];
    RACSubject *signal = [RACSubject subject];
    [signalOfSignals sendNext:signal];
    // 獲取信號中信號最近發(fā)出信號鳞芙,訂閱最近發(fā)出的信號眷柔。
    // 注意switchToLatest:只能用于信號中的信號
    
    [signalOfSignals.switchToLatest subscribeNext:^(id x) {
        
        NSLog(@"獲取信號中信號最近發(fā)出信號,訂閱最近發(fā)出的信號%@",x);
    }];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    [self.view endEditing:YES];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

9.RAC操作方法三

#import <UIKit/UIKit.h>

@interface RACOperationOrderController : UIViewController

@end
#import "RACOperationOrderController.h"

@interface RACOperationOrderController ()
@property (nonatomic, strong) RACSubject *signal;
@end

@implementation RACOperationOrderController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self initView];
    
    [self doNext];
    
    [self deliverOn];
    
    [self timeout];
    
    [self interval];
    
    [self delay];
    
    [self retry];
    
    [self replay];
    
    [self throttle];
}

- (void)initView {
    self.navigationItem.title = @"RAC操作方法三";
    self.view.backgroundColor = [UIColor whiteColor];
}

//ReactiveCocoa操作方法之秩序原朝。
- (void)doNext {

    //doNext: 執(zhí)行Next之前驯嘱,會(huì)先執(zhí)行這個(gè)Block
    //doCompleted: 執(zhí)行sendCompleted之前,會(huì)先執(zhí)行這個(gè)Block
    [[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@1];
        [subscriber sendCompleted];
        return nil;
    }] doNext:^(id x) {
        // 執(zhí)行[subscriber sendNext:@1];之前會(huì)調(diào)用這個(gè)Block
        NSLog(@"doNext");
    }] doCompleted:^{
        // 執(zhí)行[subscriber sendCompleted];之前會(huì)調(diào)用這個(gè)Block
        NSLog(@"doCompleted");
    }] subscribeNext:^(id x) {
        NSLog(@"%@",x);

    }];
}

//ReactiveCocoa操作方法之線程喳坠。
- (void)deliverOn {
    //deliverOn: 內(nèi)容傳遞切換到制定線程中鞠评,副作用在原來線程中,把在創(chuàng)建信號時(shí)block中的代碼稱之為副作用。
    
    //subscribeOn: 內(nèi)容傳遞和副作用都會(huì)切換到制定線程中。
}

//ReactiveCocoa操作方法之時(shí)間瘦馍。
- (void)timeout {
    //timeout:超時(shí)衅枫,可以讓一個(gè)信號在一定的時(shí)間后策精,自動(dòng)報(bào)錯(cuò)。
    RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        return nil;
    }] timeout:1 onScheduler:[RACScheduler currentScheduler]];
    
    [signal subscribeNext:^(id x) {
        
        NSLog(@"%@",x);

    } error:^(NSError *error) {
        // 1秒后會(huì)自動(dòng)調(diào)用
        NSLog(@"%@",error);
    }];
}

//interval 定時(shí):每隔一段時(shí)間發(fā)出信號
- (void)interval {

    [[RACSignal interval:1 onScheduler:[RACScheduler currentScheduler]] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    } error:^(NSError *error) {
         NSLog(@"%@",error);
    }];
}

//delay 延遲發(fā)送next
- (void)delay {

   [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      
        [subscriber sendNext:@1];
        return nil;
    }] delay:2] subscribeNext:^(id x) {
         NSLog(@"%@",x);
    }];
}

// ReactiveCocoa操作方法之重復(fù)政己。
- (void)retry {
    //retry重試 :只要失敗首有,就會(huì)重新執(zhí)行創(chuàng)建信號中的block,直到成功.
    __block int i = 0;
    [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        if (i == 10) {
            [subscriber sendNext:@1];
        }else{
            NSLog(@"接收到錯(cuò)誤");
            [subscriber sendError:nil];
        }
        i++;
        return nil;
        
    }] retry] subscribeNext:^(id x) {
        
        NSLog(@"%@",x);
        
    } error:^(NSError *error) {
        
        
    }];
}

//replay重放:當(dāng)一個(gè)信號被多次訂閱,反復(fù)播放內(nèi)容
- (void)replay {
    RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        
        [subscriber sendNext:@1];
        [subscriber sendNext:@2];
        
        return nil;
    }] replay];
    
    [signal subscribeNext:^(id x) {
        
        NSLog(@"第一個(gè)訂閱者%@",x);
        
    }];
    
    [signal subscribeNext:^(id x) {
        
        NSLog(@"第二個(gè)訂閱者%@",x);
        
    }];
}


- (void)throttle {
    RACSubject *signal = [RACSubject subject];
    _signal = signal;
    // 節(jié)流锻梳,在一定時(shí)間(1秒)內(nèi)抖棘,不接收任何信號內(nèi)容茂腥,過了這個(gè)時(shí)間(1秒)獲取最后發(fā)送的信號內(nèi)容發(fā)出。
    [[signal throttle:1] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}



@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末切省,一起剝皮案震驚了整個(gè)濱河市最岗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌朝捆,老刑警劉巖般渡,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異右蹦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)歼捐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門何陆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人豹储,你說我怎么就攤上這事贷盲。” “怎么了剥扣?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵巩剖,是天一觀的道長。 經(jīng)常有香客問我钠怯,道長佳魔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任晦炊,我火速辦了婚禮鞠鲜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘断国。我一直安慰自己贤姆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布稳衬。 她就那樣靜靜地躺著霞捡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪薄疚。 梳的紋絲不亂的頭發(fā)上碧信,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天赊琳,我揣著相機(jī)與錄音,去河邊找鬼音婶。 笑死慨畸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的衣式。 我是一名探鬼主播寸士,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼碴卧!你這毒婦竟也來了弱卡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤住册,失蹤者是張志新(化名)和其女友劉穎婶博,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荧飞,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凡人,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叹阔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挠轴。...
    茶點(diǎn)故事閱讀 40,503評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖耳幢,靈堂內(nèi)的尸體忽然破棺而出岸晦,到底是詐尸還是另有隱情,我是刑警寧澤睛藻,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布启上,位于F島的核電站,受9級特大地震影響店印,放射性物質(zhì)發(fā)生泄漏冈在。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一按摘、第九天 我趴在偏房一處隱蔽的房頂上張望讥邻。 院中可真熱鬧,春花似錦院峡、人聲如沸兴使。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽发魄。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間励幼,已是汗流浹背汰寓。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留苹粟,地道東北人有滑。 一個(gè)月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像嵌削,于是被迫代替她去往敵國和親毛好。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評論 2 359

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