ReactiveCocoa 入门知识——归总篇


ReactiveCocoa (RAC) 是一个Cocoa框架,受Functional Reactive Programming启发。它提供Api合成变换(composing and transforming)随着时间改变的数据流
介绍


ReactiveCocoa来源于functional reactive programming(Input and Output)。区别于使用不断变化修改的变量,RAC提供了“事件流”,通过 SignalSignalProducer 类型来表示, 它们随着时间发送值。事件流统一了Cocoa用于事件和异步处理的常用模式,包括:
委托方法
回调blocks
通知
控件的actions和响应事件链
Futures and promises
Key-value observing (KVO)

因为这些不同的机制能够用一种相同的方式处理,可以很容易的声明成链(chain)并且把它们联合在一起,用更少的代码和状态连接它们。
基本使用


1.创建信号

方法1:

   RACSubscriber:表示订阅者的意思,用于发送信号,这是一个协议,不是一个类,只要遵守这个协议,并且实现方 v     法才 能成为订阅者。通过create创建的信号,都有一个订阅者,帮助他发送数据。
 RACDisposable:用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它。
RACSignal *siganl = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    // block调用时刻:每当有订阅者订阅信号,就会调用block。
    // 2.发送信号
    [subscriber sendNext:@1];
    // 如果不在发送数据,发送信号完成,内部会自动调用[RACDisposable disposable]取消订阅信号。
    [subscriber sendCompleted];
    return [RACDisposable disposableWithBlock:^{
        // block调用时刻:当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号。
        // 执行完Block后,当前信号就不在被订阅了。
        NSLog(@"信号被销毁");
    }];
}];
// 3.订阅信号,才会激活信号.
[siganl subscribeNext:^(id x) {
    // block调用时刻:每当有信号发出数据,就会调用block.
    NSLog(@"接收到数据:%@",x);
}];
   2017-06-06 16:34:27.387 ReactiveCocoaDome[17827:6819312] 接收到数据:1
   2017-06-06 16:34:27.387 ReactiveCocoaDome[17827:6819312] 信号被销毁

方法2:

 // 1.创建信号
  RACSubject *subject = [RACSubject subject];
 // 2.订阅信号
  [subject subscribeNext:^(id x) {
    // block调用时刻:当信号发出新值,就会调用.
    NSLog(@"第一个订阅者%@",x);
 }];
 [subject subscribeNext:^(id x) {
    // block调用时刻:当信号发出新值,就会调用.
    NSLog(@"第二个订阅者%@",x);
}];
// 3.发送信号
 [subject sendNext:@"1"];
2017-06-06 16:34:27.388 ReactiveCocoaDome[17827:6819312] 第一个订阅者1
2017-06-06 16:34:27.388 ReactiveCocoaDome[17827:6819312] 第二个订阅者1

方法3:

  // RACReplaySubject使用步骤:
  // 1.创建信号 [RACSubject subject],跟RACSiganl不一样,创建信号时没有block。
  // 2.可以先订阅信号,也可以先发送信号。
  // 2.1 订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
  // 2.2 发送信号 sendNext:(id)value
  // RACReplaySubject:底层实现和RACSubject不一样。
  // 1.调用sendNext发送信号,把值保存起来,然后遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock。
  // 2.调用subscribeNext订阅信号,遍历保存的所有值,一个一个调用订阅者的nextBlock
  // 如果想当一个信号被订阅,就重复播放之前所有值,需要先发送信号,在订阅信号。
  // 也就是先保存值,在订阅值。

// 1.创建信号
RACReplaySubject *replaySubject = [RACReplaySubject subject];

// 2.发送信号
[replaySubject sendNext:@1];
[replaySubject sendNext:@2];
[replaySubject sendCompleted];

// 3.订阅信号
[replaySubject subscribeNext:^(id x) {
    
    NSLog(@"第一个订阅者接收到的数据%@",x);
}];

// 订阅信号
[replaySubject subscribeNext:^(id x) {
    
    NSLog(@"第二个订阅者接收到的数据%@",x);
}];


RACDisposable * dispose = [RACDisposable disposableWithBlock:^{
    NSLog(@"replaySubject 销毁了");
  
}];
RACCompoundDisposable * compoundDispose = [RACCompoundDisposable compoundDisposableWithDisposables:@[dispose]];
[replaySubject didSubscribeWithDisposable:compoundDispose];
 2017-06-06 16:34:27.550 ReactiveCocoaDome[17827:6819312] 第一个订阅者接收到的数据1
 2017-06-06 16:34:27.550 ReactiveCocoaDome[17827:6819312] 第一个订阅者接收到的数据2
 2017-06-06 16:34:27.551 ReactiveCocoaDome[17827:6819312] 第二个订阅者接收到的数据1
 2017-06-06 16:34:27.551 ReactiveCocoaDome[17827:6819312] 第二个订阅者接收到的数据2
 2017-06-06 16:34:27.551 ReactiveCocoaDome[17827:6819312] replaySubject 销毁了

当然 RACReplaySubject *replaySubject = [RACReplaySubject replaySubjectWithCapacity:1];
通过这种方式创建的 则指定了默认的储存值是1个
打印的 则为

 2017-06-06 16:51:00.570 ReactiveCocoaDome[18117:6914095] 第一个订阅者接收到的数据2
 2017-06-06 16:51:00.570 ReactiveCocoaDome[18117:6914095] 第二个订阅者接收到的数据2
2.针对不同对象的基本使用方法
  • UIButton
    添加事件
    [[_pushbut rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {
    @strongify(self);
    SendViewController *sendV = [[UIStoryboard storyboardWithName:@"Main" bundle:nil]instantiateViewControllerWithIdentifier:@"SendViewController"];
    sendV.delegateSingal = [RACSubject subject];
    [sendV.delegateSingal subscribeNext:^(id x) {
    NSLog(@"点击了通知按钮 %@",x);
    }];
    [self.navigationController pushViewController:sendV animated:YES];
    }];
    触发信号

     _pushbut.rac_command = [[RACCommand alloc]initWithSignalBlock:^RACSignal *(id input) {
      return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
         [subscriber sendNext:@"发送数据"];
         
         // 注意:数据传递完,最好调用sendCompleted,这时命令才执行完毕。
         [subscriber sendCompleted];
          return [RACDisposable disposableWithBlock:^{     
         }];
        }];
      }];
    
  • NSArray
    NSArray *numbers = @[@1,@2,@3,@4];
    // 这里其实是三步
    // 第一步: 把数组转换成集合RACSequence numbers.rac_sequence
    // 第二步: 把集合RACSequence转换RACSignal信号类,numbers.rac_sequence.signal
    // 第三步: 订阅信号,激活信号,会自动把集合中的所有值,遍历出来。
    [numbers.rac_sequence.signal subscribeNext:^(id x) {
    NSLog(@"数组元素 %@",x);
    }];

  • NSDictionary
    NSDictionary *dict = @{@"name":@"xmg",@"age":@18};
    [dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {

      // 解包元组,会把元组的值,按顺序给参数里面的变量赋值
      RACTupleUnpack(NSString *key,NSString *value) = x;
        // 相当于以下写法
      //        NSString *key = x[0];
      //        NSString *value = x[1];
      NSLog(@"元组对象 x == %@", x);
      NSLog(@"字典转化为 %@ %@",key,value);
     }];
    

*字典转模型 RAC高级写法 *

  // map:映射的意思,目的:把原始值value映射成一个新值
  // array: 把集合转换成数组
  // 底层实现:当信号被订阅,会遍历集合中的原始值,映射成新值,并且保存到新的数组里。
  NSArray *MMs = [[dictArr.rac_sequence map:^id(id value) {
     return [MM MMwithDic:value];
     }] array];
  • NSNotificationCenter

    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil]     
     subscribeNext:^(id x) {
      NSLog(@"键盘弹出");
     }];
    
  • UITextField
    [textF.rac_textSignal subscribeNext:^(id x) {
    NSLog(@"文字改变了%@",x);
    }];

  • 定时器

    //创建定时器信号,定时8个小时
      RACSignal *signal = [RACSignal interval:60*60*8
                                  onScheduler:[RACScheduler mainThreadScheduler]];
      //定时执行代码
      [signal subscribeNext:^(id x) {
          NSLog(@"吃药");
      }];
    
      RACSignal *singal = [[[[[RACSignal interval:1 onScheduler [RACSchedulerscheduler]]take:10]startWith:@(1)]map:^id(id value) {
      NSLog(@"%@",value);
      return @"发送出去的信号";
      }]takeUntil:self.rac_willDeallocSignal];
     [singal subscribeNext:^(id x) {
      NSLog(@"x == %@",x);
     }];
    
  • 代理
    @protocol ProgrammerDelegate
    - (void)makeAnApp;
    @end

    //为self添加一个信号,表示代理ProgrammerDelegate的makeAnApp方法信号
     RACSignal *programmerSignal = [self rac_signalForSelector:@selector(makeAnApp)
                                               fromProtocol:@protocol(ProgrammerDelegate)];
     //设置代理方法makeAnApp的实现
     [programmerSignal subscribeNext:^(RACTuple* x) {
        //这里可以理解为makeAnApp的方法要的执行代码
        NSLog(@"花了一个月,app写好了");
    }];
    //调用代理方法
    [self makeAnApp];
    
3.其他常用
  • 观察值的变化 (监控属性等 MVVM 中比较常用)

    @weakify(self);
     [RACObserve(self, value)subscribeNext:^(id x) {
      @strongify(self);
      NSLog(@"value 发生了变化%@",self.value);
      }];
    
  • 改变信号中的值
    RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    [subscriber sendNext:@"跳舞"];
    [subscriber sendCompleted];
    return [RACScopedDisposable disposableWithBlock:^{

      }];
      }];
    //对信号进行改进
     RAC(self,value) = [signalA map:^id(id value) {
      if ([value isEqualToString:@"跳舞"]) {
          return @"唱歌";
      }
      return @"";
    }];
    
  • 广播 通知

    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    //注册广播通知
    RACSignal *signal = [center rac_addObserverForName:@"代码之道频道" object:nil];
     //设置接收到通知的回调处理
    [signal subscribeNext:^(NSNotification* x) {
      NSLog(@"技巧:%@", x.userInfo[@"技巧"]);
      }];
     //发送广播通知
     [center postNotificationName:@"代码之道频道"
                        object:nil
                      userInfo:@{@"技巧":@"用心写"}];
    
  • 串联 并联
    //创建一个信号管A
    {
    RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    //发送一个Next玻璃球和一个Complete玻璃球
    [subscriber sendNext:@"我恋爱啦"];
    [subscriber sendCompleted];
    return nil;
    }];
    //创建一个信号管B
    RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    //发送一个Next玻璃球和一个Complete玻璃球
    [subscriber sendNext:@"我结婚啦"];
    [subscriber sendCompleted];
    return nil;
    }];
    //串联管A和管B
    RACSignal *concatSignal = [signalA concat:signalB];
    //串联后的接收端处理
    [concatSignal subscribeNext:^(id x) {
    NSLog(@"%@",x);
    }];
    //打印:我恋爱啦 我结婚啦

     }
     {
      //创建信号A
      RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id subscriber) {
          [subscriber sendNext:@"纸厂污水"];
          return nil;
      }];
      //创建信号B
      RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id subscriber) {
          [subscriber sendNext:@"电镀厂污水"];
          return nil;
      }];
      //并联2个信号
      RACSignal *mergeSignal = [RACSignal merge:@[signalA, signalB]];
      [mergeSignal subscribeNext:^(id x) {
          NSLog(@"处理%@",x);
      }];
     }
    
  • 组合
    {
    //定义2个自定义信号
    RACSubject *letters = [RACSubject subject];
    RACSubject *numbers = [RACSubject subject];
    //组合信号
    [[RACSignal combineLatest:@[letters, numbers]
    reduce:^(NSString *letter, NSString *number){
    //把2个信号的信号值进行字符串拼接
    return [letter stringByAppendingString:number];
    }] subscribeNext:^(NSString * x) {
    NSLog(@"%@", x);
    }];
    //自己控制发送信号值
    [letters sendNext:@"A"];
    [letters sendNext:@"B"];
    [numbers sendNext:@"1"];//打印B1
    [letters sendNext:@"C"];//打印C1
    [numbers sendNext:@"2"];//打印C2
    }

  • 映射
    //创建信号,发送"石"玻璃球
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    [subscriber sendNext:@"石"];
    return nil;
    }];
    //对信号进行改造,改造"石"为"金"
    signal = [signal map:^id(NSString *value) {
    if ([value isEqualToString:@"石"]) {
    return @"金";
    }
    return value;
    }];
    打印
    [signal subscribeNext:^(id x) {
    NSLog(@"%@", x);//金
    }];

  • 过滤

    //创建信号
      RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
          [subscriber sendNext:@(15)];
          [subscriber sendNext:@(17)];
          [subscriber sendNext:@(21)];
          [subscriber sendNext:@(14)];
          [subscriber sendNext:@(30)];
          return nil;
      }];
      //过滤信号,并打印
      [[signal filter:^BOOL(NSNumber* value) {
          //值大于等于18的才能通过过滤网
          return value.integerValue >= 18;
      }] subscribeNext:^(id x) {
          NSLog(@"%@", x);
      }];
      //打印:21 30
    
  • 命令
    //创建命令
    RACCommand *aCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(NSString *input) {
    //命令执行代码
    NSLog(@"%@我投降了",input);
    //返回一个RACSignal信号
    return [RACSignal createSignal:^RACDisposable *(id subscriber) {
    [subscriber sendCompleted];
    return nil;
    }];
    }];
    //执行命令
    [aCommand execute:@"今天"];
    //打印:今天我投降了

  • 延迟
    //创建一个信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    NSLog(@"等等我,我还有10秒钟就到了");
    [subscriber sendNext:@"天河中心"];
    [subscriber sendCompleted];
    return nil;
    }];
    //延时10秒接受Next玻璃球
    [[signal delay:10] subscribeNext:^(NSString x) {
    NSLog(@"我到了%@",x);
    }];
    /

    [2016-04-21 13:20:10]等等我,我还有10秒钟就到了
    [2016-04-21 13:20:20]我到了天河中心
    */

  • 超时处理
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    //创建发送信息信号
    NSLog(@"我快到了");
    RACSignal sendSignal = [RACSignal createSignal:^RACDisposable (id sendSubscriber) {
    [sendSubscriber sendNext:nil];
    [sendSubscriber sendCompleted];
    return nil;
    }];
    //发送信息要1个小时10分钟才到
    [[sendSignal delay:60
    70] subscribeNext:^(id x) {
    //这里才发送Next玻璃球到signal
    [subscriber sendNext:@"我到了"];
    [subscriber sendCompleted];
    }];
    return nil;
    }];
    //这里对signal进行超时接受处理
    [[signal timeout:60
    60
    onScheduler:[RACScheduler mainThreadScheduler] ]
    subscribeError:^(NSError *error)
    {
    //超时错误处理
    NSLog(@"等了你一个小时了,你还没来,我走了");
    }];

  • 重试
    __block int failedCount = 0;
    //创建信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    if (failedCount < 100) {
    failedCount++;
    NSLog(@"我失败了");
    //发送错误,才会要重试
    [subscriber sendError:nil];
    } else {
    NSLog(@"经历了数百次失败后");
    [subscriber sendNext:nil];
    }
    return nil;
    }];
    //重试
    RACSignal *retrySignal = [signal retry];
    //直到发送了Next玻璃球
    [retrySignal subscribeNext:^(id x) {
    NSLog(@"终于成功了");
    }];

  • 节流
    RACSignal *signal = [RACSignal createSignal:^RACDisposable (id subscriber) {
    //即时发送一个Next玻璃球
    [subscriber sendNext:@"A"];
    //下面是GCD延时发送Next玻璃球
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(1 * NSEC_PER_SEC)),mainQueue, ^{
    [subscriber sendNext:@"B"];
    });
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2 * NSEC_PER_SEC)),mainQueue, ^{
    //发送多个Next,如果节流了,接收最新发送的
    [subscriber sendNext:@"C"];
    [subscriber sendNext:@"D"];
    [subscriber sendNext:@"E"];
    });
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(3 * NSEC_PER_SEC)),mainQueue, ^{
    [subscriber sendNext:@"F"];
    });
    return nil;
    }];
    //对信号进行节流,限制短时间内一次只能接收一个Next玻璃球
    [[signal throttle:1] subscribeNext:^(id x) {
    NSLog(@"%@通过了",x);
    }];
    /

    A
    B
    E
    F
    *

  • 条件 (直到什么时候才 takeUntil)
    //创建取值信号
    RACSignal *takeSignal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    //创建一个定时器信号,每隔1秒触发一次
    RACSignal *signal = [RACSignal interval:1
    onScheduler:[RACScheduler mainThreadScheduler]];
    //定时接收
    [signal subscribeNext:^(id x) {

              [subscriber sendNext:@"我不玩游戏除非你求我"];
          }];
          return nil;
      }];
      //创建条件信号
      RACSignal *conditionSignal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
          //设置5秒后发生Complete玻璃球
      
          dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
                                       (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
              NSLog(@"我求你了");
              [subscriber sendCompleted];
          });
          return nil;
      }];
      //设置条件,takeSignal信号在conditionSignal信号接收完成前,不断地取值
      [[takeSignal takeUntil:conditionSignal] subscribeNext:^(id x) {
          NSLog(@"%@", x);
      }];
      /*
      我不玩游戏除非你求我
      我不玩游戏除非你求我  
      我不玩游戏除非你求我  
      我不玩游戏除非你求我  
      我不玩游戏除非你求我 
      我求你了  
       */
    
4.常用宏
    // RAC 用于给某个对象的属性绑定
RAC(self.sendBut.titleLabel,text) = self.textF.rac_textSignal;

//RACObserve(self, name):监听某个对象的某个属性,返回的是信号。
[RACObserve(self.view, center)subscribeNext:^(id x) {
    NSLog(@"%@",x);
}];
@weakify(self); @strongify(self);
// RACTuplePack:把数据包装成RACTuple(元组类)
// 把参数中的数据包装成元组
RACTuple *tuple1 = RACTuplePack(@10,@20);
 //RACTupleUnpack:把RACTuple(元组类)解包成对应的数据。
  // 把参数中的数据包装成元组
  RACTuple *tuple = RACTuplePack(@"img",@20);
 // 解包元组,会把元组的值,按顺序给参数里面的变量赋值
 // name = @"xmg" age = @20
 RACTupleUnpack(NSString *name,NSNumber *age) = tuple;
5.项目中使用示例

网络请求
- (RACSubject *)POST:(NSString *)path parameters:(id)parameters
{
RACReplaySubject *signal = [RACReplaySubject replaySubjectWithCapacity:1];
NSURLSessionDataTask *task = [[PR_HTTPSessionManager defaultManager] POST:path parameters:[[self class] addPublicParameters:parameters] success:^(NSURLSessionDataTask *task, id responseObject){
NSDictionary * responseDict = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:nil];

        NSError *error = [[self class] errorForResponseObject:responseDict];
        if (error) {
             [signal sendError:error];
        } else {
            id data = responseDict[@"results"];
            [signal sendNext:data];
            [signal sendCompleted];
        }
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        
        [signal sendError:[[self class] localizedError:error]];
    }];
  
@weakify(task);
RACDisposable * dispose = [RACDisposable disposableWithBlock:^{
    @strongify(task);
    [task cancel];
}];

RACCompoundDisposable * compoundDispose = [RACCompoundDisposable compoundDisposableWithDisposables:@[dispose]];
[signal didSubscribeWithDisposable:compoundDispose];

return signal;
}


  + (RACSignal *)updateUserInfo:(NSDictionary *)params {
RACSignal *signal = [RACSignal empty];
NSString *method = @"xxxxxx";
signal =
    [[PR_HTTPSessionManager defaultManager] POST:method parameters:params];
return signal;
}  

一段赋值代码

 @weakify(self);
 RAC(self.suggestTitleLabel,text) = RACObserve(self, viewModel.title);
 [[RACObserve(self, viewModel) ignore:nil] subscribeNext:^(DoctorSuggestTableViewCellModel  *cellViewModel) {
    @strongify(self);
    [self.textField setText:cellViewModel.contentString];
    self.suggestCollectionView.allowsMultipleSelection = cellViewModel.allowsMultipleSelection;
    
    [[self.textField.rac_textSignal takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(id x) {
        @strongify(self);
        self.viewModel.contentString = x;
    }];
     [[RACObserve(self.otherTextView,content) takeUntil:self.rac_prepareForReuseSignal]subscribeNext:^(id x)       {
        @strongify(self);
        self.viewModel.descriptionString = x;
    }];

}]; 

当然还有很多使用到的地方就不在累述 Dome地址

以上都是入门级的使用,希望可以对你有所帮助!

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

推荐阅读更多精彩内容