RAC

RACSubject

//创建信号
RACSubject *subject = [RACSubject subject];
//订阅信号
[subject subscribeNext:^(id  _Nullable x) {
      NSLog(@"subscribeNext %@",x);
}];
//发送信号 
[subject sendNext:@"1"];

分析

step1: 先看下RACSubject

@interface RACSubject<ValueType> : RACSignal<ValueType> <RACSubscriber>

/// Returns a new subject.
+ (instancetype)subject;

// Redeclaration of the RACSubscriber method. Made in order to specify a generic type.
- (void)sendNext:(nullable ValueType)value;

@end

发现RACSubject继承于RACSignal,有一个初始化方法subject和一个发送信号的方法 sendNext

step2: 查看subject方法

+ (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;
}

调用了[[self alloc] init]方法,并对init方法进行重写,在init中对两个属性进行了初始化

  • 看下属性的声明
// Contains all current subscribers to the receiver.
//
// This should only be used while synchronized on `self`.
@property (nonatomic, strong, readonly) NSMutableArray *subscribers;

// Contains all of the receiver's subscriptions to other signals.
@property (nonatomic, strong, readonly) RACCompoundDisposable *disposable;

用一个数组保存所有的订阅者,另一个对象用来取消订阅

step3: 查看订阅subscribeNext:方法,这是父类的方法

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);
    
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    return [self subscribe:o];
}
  • 创建订阅者
+ (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;
}

创建一个订阅者,将传进来的参数赋值给订阅者的成员变量,在例子中就是将订阅的block与订阅者关联上。

  • subscribe 调用的是RACSubject类中的subscribe
- (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;
}

将上一步创建的订阅者保存在第一步创建的数组中

step4: 查看sendNext:方法

- (void)sendNext:(id)value {
    [self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
        [subscriber sendNext:value];
    }];
}

遍历所有的订阅者

  • 查看遍历方法
- (void)enumerateSubscribersUsingBlock:(void (^)(id<RACSubscriber> subscriber))block {
    NSArray *subscribers;
    @synchronized (self.subscribers) {
        subscribers = [self.subscribers copy];
    }

    for (id<RACSubscriber> subscriber in subscribers) {
        block(subscriber);
    }
}

self.subscribers是第一步中创建的数组,遍历数组将其中的订阅者对象取出来,调用传进来的block方法。

  • 查看遍历回调中的sendNext,这是调用的RACSubscriber的方法
- (void)sendNext:(id)value {
    @synchronized (self) {
        void (^nextBlock)(id) = [self.next copy];
        if (nextBlock == nil) return;

        nextBlock(value);
    }
}

从订阅者对象中取出next变量,即第二步中订阅信号时的block,然后调用这个block方法

RACSignal

既然从前面得知RACSubject继承于RACSignal,那我们就研究下RACSignal

// 1.创建信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // 3.发送信号
        [subscriber sendNext:@"liu"];
        //发送完成信号 并取消订阅
        [subscriber sendCompleted];
        // 4.取消信号,如果信号想要被取消,就必须返回一个RACDisposable
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"取消订阅");
        }];
    }];
    
    [signal subscribeNext:^(id x) {
        // block的调用时刻:只要信号内部发出数据就会调用这个block
        NSLog(@"======%@", x);
    }];

运行结果

源码分析

step1: 查看createSignal方法

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    return [RACDynamicSignal createSignal:didSubscribe];
}

//简单看下 RACDynamicSignal
//一个私有的RACSignal子类实现它们的订阅行为
// A private `RACSignal` subclasses that implements its subscription behavior
// using a block.
@interface RACDynamicSignal : RACSignal

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe;

@end
  • 查看RACDynamicSignal类中的createSignal方法
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    RACDynamicSignal *signal = [[self alloc] init];
    signal->_didSubscribe = [didSubscribe copy];
    return [signal setNameWithFormat:@"+createSignal:"];
}

这个方法中主要做了两件事

  • 1、创建一个信号量对象
  • 2、将外面传进来的block保存在信号量对象的_didSubscribe变量中

step2: 查看订阅信号subscribeNext

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

这个方法主要做了两件事

  • 1、创建订阅者

  • 2、执行订阅命令

  • subscriberWithNext: error: completed:前面已经讲过,这里就不再讲述

  • 查看subscribe:方法,是在RACDynamicSignal类中

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

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

    if (self.didSubscribe != NULL) {
        RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
            RACDisposable *innerDisposable = self.didSubscribe(subscriber);
            [disposable addDisposable:innerDisposable];
        }];

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

关键代码在self.didSubscribe(subscriber);,在创建信号量时将传进来的block 赋值给了self.didSubscribe,此时调用self.didSubscribe(subscriber);即调用block并将订阅者对象传进去

问题是self.didSubscribe(subscriber);的调用这是在block中,那这个block是在什么时候调用?

  • 查看schedule方法,是RACSubscriptionScheduler类的方法
- (RACDisposable *)schedule:(void (^)(void))block {
    NSCParameterAssert(block != NULL);

    if (RACScheduler.currentScheduler == nil) return [self.backgroundScheduler schedule:block];

    block();
    return nil;
}

可以看到传进来的参数block被调用

step3: sendNext发送信号

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

        nextBlock(value);
    }
}

从订阅者对象中取出next变量,即第二步中订阅信号时的block,然后调用这个block方法

RACReplaySubject

RACReplaySubject *subject = [RACReplaySubject subject];
[subject subscribeNext:^(id  _Nullable x) {
     NSLog(@"subscribeNext - %@",x);
}];
[subject sendNext:@"liu"];

运行结果

subscribeNext - liu

源码分析

step1: 先看下它的.h文件

保存它发送的值
/// A replay subject saves the values it is sent (up to its defined capacity)
/// and resends those to new subscribers. It will also replay an error or
/// completion.
@interface RACReplaySubject<ValueType> : RACSubject<ValueType>

/// Creates a new replay subject with the given capacity. A capacity of
/// RACReplaySubjectUnlimitedCapacity means values are never trimmed.
+ (instancetype)replaySubjectWithCapacity:(NSUInteger)capacity;

@end

RACReplaySubjectRACSubject的子类,对外只暴露了一个replaySubjectWithCapacity方法,这是个初始化方法

step2: 看下replaySubjectWithCapacity方法实现

+ (instancetype)replaySubjectWithCapacity:(NSUInteger)capacity {
    return [(RACReplaySubject *)[self alloc] initWithCapacity:capacity];
}

- (instancetype)init {
    return [self initWithCapacity:RACReplaySubjectUnlimitedCapacity];
}

- (instancetype)initWithCapacity:(NSUInteger)capacity {
    self = [super init];
    
    _capacity = capacity;
    _valuesReceived = (capacity == RACReplaySubjectUnlimitedCapacity ? [NSMutableArray array] : [NSMutableArray arrayWithCapacity:capacity]);
    
    return self;
}

不管是通过自己的初始化方法replaySubjectWithCapacity,还是通过父类的初始化方法subject,最终都会调用initWithCapacity方法,在initWithCapacity方法里创建了一个数组用来存储发送信号时传递的数据。

step2: 订阅信号subscribeNext

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);
    
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    return [self subscribe:o];
}
  • 第一步创建订阅者与父类是相同的,创建一个订阅者并将block保存在订阅者的成员变量中。

  • 第二步调用的RACReplaySubject自己的方法

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];

    RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
        @synchronized (self) {
            for (id value in self.valuesReceived) {
                if (compoundDisposable.disposed) return;

                [subscriber sendNext:(value == RACTupleNil.tupleNil ? nil : value)];
            }

            if (compoundDisposable.disposed) return;

            if (self.hasCompleted) {
                [subscriber sendCompleted];
            } else if (self.hasError) {
                [subscriber sendError:self.error];
            } else {
                RACDisposable *subscriptionDisposable = [super subscribe:subscriber];
                [compoundDisposable addDisposable:subscriptionDisposable];
            }
        }
    }];

    [compoundDisposable addDisposable:schedulingDisposable];

    return compoundDisposable;
}

会遍历第一步创建的数组保存的数据,然后发送信号,即订阅信号流程中包含发送信号

step3: 发送信号sendNext,注意这是在RACReplaySubject中实现

- (void)sendNext:(id)value {
    @synchronized (self) {
        [self.valuesReceived addObject:value ?: RACTupleNil.tupleNil];
        
        if (self.capacity != RACReplaySubjectUnlimitedCapacity && self.valuesReceived.count > self.capacity) {
            [self.valuesReceived removeObjectsInRange:NSMakeRange(0, self.valuesReceived.count - self.capacity)];
        }
        
        [super sendNext:value];
    }
}

会将发送的数据保存在第一步创建的数组中以便订阅信号时使用,最终发送信号还是调用的父类的方法。

问题: RACReplaySubjectRACSubject有什么区别

RACSubject中代码必须按照 创建信号--> 订阅信号 --> 发送信号,这样的先后顺序,否则无效;而在RACReplaySubject创建信号后,订阅信号发送信号顺序无所谓。

所以RACReplaySubject写成下面这样也是可以的。

RACReplaySubject *subject = [RACReplaySubject subject];
[subject sendNext:@"liu"];
[subject subscribeNext:^(id  _Nullable x) {
      NSLog(@"subscribeNext - %@",x);
}];

RACCommand

- (void)testRACCommand {
    
    /**
     *  RACCommand使用注意
     *  1、RACCommand内部必须返回RACSignal
     *  2、executionSignals信号中的信号,一开始获取不到内部信号
     *      2.1 使用switchToLatest:获取内部信号
     *      2.2 使用execute:获取内部信号
     *  3、executing判断是否正在执行
     *      3.1 第一次不准确,需要skip:跳过
     *      3.2 一定要记得sendCompleted,否则永远不会执行完成
     *  4、通过执行execute,执行command的block
     */
    
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"发送第1条信号"];
            [subscriber sendCompleted];
            return nil;
        }];
    }];
    
    [command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {

    }];
    
    [[command.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) {
        //NSLog(@"x = %@", x);
        if (x.boolValue) {
            NSLog(@"x = %@ 正在执行", x);
        } else {
            NSLog(@"x = %@ 执行完成", x);
        }
    }];
    
    [command execute:@1];
    
}

源码分析

step1: 查看RACCommand类的.h文件

1. executionSignals:需要执行的block成功的时候返回的信号,他是在主线程执行的。
2. executing:信号量  监听RACCommand的状态
3. enabled:当前命令是否enabled,默认是no,他也可以根据enableSignal来设置或者allowsConcurrentExecution设置为NO的时候(command已经开始执行)
4. errors:执行command的时候获取的error都会通过这个信号发送
5. allowsConcurrentExecution:是否允许并发执行command,默认是NO。

6.initWithSignalBlock:(RACSignal * (^)(id input))signalBlock:初始化RACCommand,参数为返回一个信号的block,即block返回的是executionSignals
7.- (id)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(id input))signalBlock:第一个参数设置当前command是否可用,第二个是执行的block。enableed默认是yes,所以第二个参数也可以为nil。
8.execute:(id)input:调用command,input为executionSignals的订阅者发送的值

step2: 查看初始化方法

- (instancetype)initWithSignalBlock:(RACSignal<id> * (^)(id input))signalBlock {
    return [self initWithEnabled:nil signalBlock:signalBlock];
}

- (instancetype)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal<id> * (^)(id input))signalBlock {
    NSCParameterAssert(signalBlock != nil);

    self = [super init];

    _addedExecutionSignalsSubject = [RACSubject new];
    _allowsConcurrentExecutionSubject = [RACSubject new];
    _signalBlock = [signalBlock copy];

    _executionSignals = [[[self.addedExecutionSignalsSubject
        map:^(RACSignal *signal) {
            return [signal catchTo:[RACSignal empty]];
        }]
        deliverOn:RACScheduler.mainThreadScheduler]
        setNameWithFormat:@"%@ -executionSignals", self];
    
    // `errors` needs to be multicasted so that it picks up all
    // `activeExecutionSignals` that are added.
    //
    // In other words, if someone subscribes to `errors` _after_ an execution
    // has started, it should still receive any error from that execution.
    RACMulticastConnection *errorsConnection = [[[self.addedExecutionSignalsSubject
        flattenMap:^(RACSignal *signal) {
            return [[signal
                ignoreValues]
                catch:^(NSError *error) {
                    return [RACSignal return:error];
                }];
        }]
        deliverOn:RACScheduler.mainThreadScheduler]
        publish];
    
    _errors = [errorsConnection.signal setNameWithFormat:@"%@ -errors", self];
    [errorsConnection connect];

    RACSignal *immediateExecuting = [[[[self.addedExecutionSignalsSubject
        flattenMap:^(RACSignal *signal) {
            return [[[signal
                catchTo:[RACSignal empty]]
                then:^{
                    return [RACSignal return:@-1];
                }]
                startWith:@1];
        }]
        scanWithStart:@0 reduce:^(NSNumber *running, NSNumber *next) {
            return @(running.integerValue + next.integerValue);
        }]
        map:^(NSNumber *count) {
            return @(count.integerValue > 0);
        }]
        startWith:@NO];

    _executing = [[[[[immediateExecuting
        deliverOn:RACScheduler.mainThreadScheduler]
        // This is useful before the first value arrives on the main thread.
        startWith:@NO]
        distinctUntilChanged]
        replayLast]
        setNameWithFormat:@"%@ -executing", self];
    
    RACSignal *moreExecutionsAllowed = [RACSignal
        if:[self.allowsConcurrentExecutionSubject startWith:@NO]
        then:[RACSignal return:@YES]
        else:[immediateExecuting not]];
    
    if (enabledSignal == nil) {
        enabledSignal = [RACSignal return:@YES];
    } else {
        enabledSignal = [enabledSignal startWith:@YES];
    }
    
    _immediateEnabled = [[[[RACSignal
        combineLatest:@[ enabledSignal, moreExecutionsAllowed ]]
        and]
        takeUntil:self.rac_willDeallocSignal]
        replayLast];
    
    _enabled = [[[[[self.immediateEnabled
        take:1]
        concat:[[self.immediateEnabled skip:1] deliverOn:RACScheduler.mainThreadScheduler]]
        distinctUntilChanged]
        replayLast]
        setNameWithFormat:@"%@ -enabled", self];

    return self;
}

将传进来的block拷贝到变量_signalBlock 中,以便后面使用。

step3: 查看command.executing subscribeNext:,订阅信号从而获取RACCommand的状态

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

点击subscribeNext进入的是RACSignal文件,但是查看self会发现是RACReplaySubject

  • 进入RACReplaySubjectsubscribe方法
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];

    RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
        @synchronized (self) {
            for (id value in self.valuesReceived) {
                if (compoundDisposable.disposed) return;

                [subscriber sendNext:(value == RACTupleNil.tupleNil ? nil : value)];
            }

            if (compoundDisposable.disposed) return;

            if (self.hasCompleted) {
                [subscriber sendCompleted];
            } else if (self.hasError) {
                [subscriber sendError:self.error];
            } else {
                RACDisposable *subscriptionDisposable = [super subscribe:subscriber];
                [compoundDisposable addDisposable:subscriptionDisposable];
            }
        }
    }];

    [compoundDisposable addDisposable:schedulingDisposable];

    return compoundDisposable;
}

step4: RACCommand执行命令execute

- (RACSignal *)execute:(id)input {
    // `immediateEnabled` is guaranteed to send a value upon subscription, so
    // -first is acceptable here.
    BOOL enabled = [[self.immediateEnabled first] boolValue];
    if (!enabled) {
        NSError *error = [NSError errorWithDomain:RACCommandErrorDomain code:RACCommandErrorNotEnabled userInfo:@{
            NSLocalizedDescriptionKey: NSLocalizedString(@"The command is disabled and cannot be executed", nil),
            RACUnderlyingCommandErrorKey: self
        }];

        return [RACSignal error:error];
    }

    RACSignal *signal = self.signalBlock(input);
    NSCAssert(signal != nil, @"nil signal returned from signal block for value: %@", input);

    // We subscribe to the signal on the main thread so that it occurs _after_
    // -addActiveExecutionSignal: completes below.
    //
    // This means that `executing` and `enabled` will send updated values before
    // the signal actually starts performing work.
    RACMulticastConnection *connection = [[signal
        subscribeOn:RACScheduler.mainThreadScheduler]
        multicast:[RACReplaySubject subject]];
    
    [self.addedExecutionSignalsSubject sendNext:connection.signal];

    [connection connect];
    return [connection.signal setNameWithFormat:@"%@ -execute: %@", self, RACDescription(input)];
}
  • self.signalBlock(input)self.signalBlock是第一步中外部传进来的block,这句代码就是执行外面的block

RACTuple

CocoaReactive 中,有些信号流传递的信号量是由其他多个信号流传递的信号量组合而成的,而这多个信号量则是封装在一个 RACTuple 实例中进行传递的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容