ReactiveCocoa用法幫助

ReactiveCocoa框架概覽

網(wǎng)上有個(gè)說法對(duì)有個(gè)ReactiveCocoa很好的理解:

可以把信號(hào)想象成水龍頭穴豫,只不過里面不是水怀大,而是玻璃球(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è)改動(dòng)裝置,把球改變成符合自己的需求(map)鸭丛。也可以把多個(gè)水龍頭合并成一個(gè)新的水龍頭(combineLatest:reduce:)竞穷,這樣只要其中的一個(gè)水龍頭有玻璃球出來,這個(gè)新合并的水龍頭就會(huì)得到這個(gè)球鳞溉。

下面我來逐一介紹ReactiveCocoa框架的每個(gè)組件

Streams

Streams 表現(xiàn)為RACStream類瘾带,可以看做是水管里面流動(dòng)的一系列玻璃球,它們有順序的依次通過熟菲,在第一個(gè)玻璃球沒有到達(dá)之前看政,你沒法獲得第二個(gè)玻璃球朴恳。
RACStream描述的就是這種線性流動(dòng)玻璃球的形態(tài),比較抽象允蚣,它本身的使用意義并不很大于颖,一般會(huì)以signals或者sequences等這些更高層次的表現(xiàn)形態(tài)代替。

Signals

Signals 表現(xiàn)為RACSignal類嚷兔,就是前面提到水龍頭森渐,ReactiveCocoa的核心概念就是Signal,它一般表示未來要到達(dá)的值冒晰,想象玻璃球一個(gè)個(gè)從水龍頭里出來同衣,只有了接收方(subscriber)才能獲取到這些玻璃球(value)。

Signal會(huì)發(fā)送下面三種事件給它的接受方(subscriber)壶运,想象成水龍頭有個(gè)指示燈來匯報(bào)它的工作狀態(tài)耐齐,接受方通過-subscribeNext:error:completed:對(duì)不同事件作出相應(yīng)反應(yīng)

  • next 從水龍頭里流出的新玻璃球(value)
  • error 獲取新的玻璃球發(fā)生了錯(cuò)誤,一般要發(fā)送一個(gè)NSError對(duì)象蒋情,表明哪里錯(cuò)了
  • completed 全部玻璃球已經(jīng)順利抵達(dá)埠况,沒有更多的玻璃球加入了

一個(gè)生命周期的Signal可以發(fā)送任意多個(gè)“next”事件,和一個(gè)“error”或者“completed”事件(當(dāng)然“error”和“completed”只可能出現(xiàn)一種)

Subjects

subjects 表現(xiàn)為RACSubject類棵癣,可以認(rèn)為是“可變的(mutable)”信號(hào)/自定義信號(hào)辕翰,它是嫁接非RAC代碼到Signals世界的橋梁,很有用浙巫。嗯金蜀。。的畴。 這樣講還是很抽象渊抄,舉個(gè)例子吧:

RACSubject *letters = [RACSubject subject];
RACSignal *signal = [letters sendNext:@"a"];

可以看到@"a"只是一個(gè)NSString對(duì)象,要想在水管里順利流動(dòng)丧裁,就要借RACSubject的力护桦。

Commands

與RACSignal 等元素不同,RACCommand 并不表示數(shù)據(jù)流煎娇,它只是一個(gè)繼承自 NSObject 的類二庵,但是它卻可以用來創(chuàng)建和訂閱用于響應(yīng)某些事件的信號(hào)。

@interface RACCommand<__contravariant InputType, __covariant ValueType> : NSObject

@end

它本身并不是一個(gè) RACStream 或者 RACSignal 的子類缓呛,而是一個(gè)用于管理 RACSignal 的創(chuàng)建與訂閱的類催享。

在 ReactiveCocoa 中的 FrameworkOverview 部分對(duì) RACCommand 有這樣的解釋:

A command, represented by the RACCommand class, creates and subscribes to a signal in response to some action. This makes it easy to perform side-effecting work as the user interacts with the app.

在用于與 UIKit 組件進(jìn)行交互或者執(zhí)行包含副作用的操作時(shí),RACCommand 能夠幫助我們更快的處理并且響應(yīng)任務(wù)哟绊,減少編碼以及工程的復(fù)雜度因妙。

直接舉個(gè)例子吧,比如一個(gè)簡(jiǎn)單的注冊(cè)界面:

RACSignal *formValid=[RACSignal
    combineLatest:@[
        self.userNameField.rac_textSignal,
        self.emailField.rac_textSignal,
    ]
    reduce:^(NSString *userName,NSString *email){
        return@(userName.length&gt;0
                &amp;&amp;email.length&gt;0);
    }];

RACCommand *createAccountCommand = [RACCommand commandWithCanExecuteSignal:formValid];
RACSignal *networkResults=[[[createAccountCommand
  addSignalBlock:^RACSignal *(idvalue){
      //... 網(wǎng)絡(luò)交互代碼
}] switchToLatest] deliverOn:[RACSchedulermainThreadScheduler]];

// 綁定創(chuàng)建按鈕的 UI state 和點(diǎn)擊事件
[[self.createButtonrac_signalForControlEvents:UIControlEventTouchUpInside]executeCommand:createAccountCommand];
Sequences

sequence 表現(xiàn)為RACSequence類,可以簡(jiǎn)單看做是RAC世界的NSArray攀涵,RAC增加了-rac_sequence方法铣耘,可以使諸如NSArray這些集合類(collection classes)直接轉(zhuǎn)換為RACSequence來使用。

Schedulers

scheduler 表現(xiàn)為RACScheduler類以故,類似于GCD;

but schedulers support cancellationbut schedulers support cancellation, and always execute serially.

ReactiveCocoa的簡(jiǎn)單使用

實(shí)踐出真知蜗细,下面就舉一些簡(jiǎn)單的例子,一起看看RAC的使用

Subscription

接收 -subscribeNext: -subscribeError: -subscribeCompleted:

RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;

// 依次輸出 A B C D…
[letters subscribeNext:^(NSString *x) {
    NSLog(@"%@", x);
}];
Injecting effects

注入效果 -doNext: -doError: -doCompleted:怒详,看下面注釋應(yīng)該就明白了:

__block unsigned subscriptions=0;

RACSignal *loggingSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    subscriptions++;
    [subscriber sendCompleted];
    returnnil;
}];

// 不會(huì)輸出任何東西
loggingSignal=[loggingSignal doCompleted:^{
    NSLog(@"about to complete subscription %u",subscriptions);
}];

// 輸出:
// about to complete subscription 1
// subscription 1
[loggingSignal subscribeCompleted:^{
    NSLog(@"subscription %u",subscriptions);
}];
Mapping

-map: 映射炉媒,可以看做對(duì)玻璃球的變換、重新組裝

RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;
 // Contains: AA BB CC DD EE FF GG HH II
RACSequence *mapped = [letters map:^(NSString *value) {
   return [value stringByAppendingString:value];
}];
Filtering

-filter:過濾棘利,不符合要求的玻璃球不允許通過

RACSequence *numbers=[@"1 2 3 4 5 6 7 8 9"componentsSeparatedByString:@" "].rac_sequence;

// Contains: 2 4 6 8
RACSequence *filtered=[numbers filter:^BOOL(NSString *value){
    return(value.intValue%2)==0;
}];
Concatenating

-concat: 把一個(gè)水管拼接到另一個(gè)水管之后

RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;

// Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9
RACSequence *concatenated = [letters concat:numbers];
Flattening

-flatten: Sequences are concatenated

RACSequence *letters=[@"A B C D E F G H I"componentsSeparatedByString:@" "].rac_sequence;
RACSequence *numbers=[@"1 2 3 4 5 6 7 8 9"componentsSeparatedByString:@" "].rac_sequence;
RACSequence *sequenceOfSequences = @[letters,numbers].rac_sequence;

// Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9
RACSequence *flattened=[sequenceOfSequences flatten];

Signals are merged (merge可以理解成把幾個(gè)水管的龍頭合并成一個(gè)橱野,哪個(gè)水管中的玻璃球哪個(gè)先到先吐哪個(gè)玻璃球)

RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSignal *signalOfSignals = [RACSignal createSignal:^ RACDisposable * (id&lt;RACSubscriber&gt; subscriber) {
    [subscriber sendNext:letters];
    [subscriber sendNext:numbers];
    [subscriber sendCompleted];
    return nil;
}];

RACSignal *flattened = [signalOfSignals flatten];

// Outputs: A 1 B C 2
[flattened subscribeNext:^(NSString *x) {
    NSLog(@"%@", x);
}];

[letters sendNext:@"A"];
[numbers sendNext:@"1"];
[letters sendNext:@"B"];
[letters sendNext:@"C"];
[numbers sendNext:@"2"];
Mapping and flattening

-flattenMap:先 map 再 flatten

RACSequence *numbers=[@"1 2 3 4 5 6 7 8 9"componentsSeparatedByString:@" "].rac_sequence;

// Contains: 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9
RACSequence *extended=[numbers flattenMap:^(NSString *num){
    return@[num,num].rac_sequence;
}];

// Contains: 1_ 3_ 5_ 7_ 9_
RACSequence *edited=[numbers flattenMap:^(NSString *num){
    if(num.intValue%2==0){
        return[RACSequenceempty];
    }else{
        NSString *newNum=[num stringByAppendingString:@"_"];
        return[RACSequencereturn:newNum];
    }
}];

RACSignal *letters=[@"A B C D E F G H I"componentsSeparatedByString:@" "].rac_sequence.signal;

[[letters flattenMap:^(NSString *letter){
    return[databasesaveEntriesForLetter:letter];
}] subscribeCompleted:^{
    NSLog(@"All database entries saved successfully.");
}];
Sequencing

-then: 會(huì)等待completed事件的發(fā)送朽缴,然后再訂閱由then block返回的signal善玫。這樣就高效地把控制權(quán)從一個(gè)signal傳遞給下一個(gè)。

RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;

// 新水龍頭只包含: 1 2 3 4 5 6 7 8 9
//
// 但當(dāng)有接收時(shí)密强,仍會(huì)執(zhí)行舊水龍頭doNext的內(nèi)容茅郎,所以也會(huì)輸出 A B C D E F G H I
RACSignal *sequenced = [[letters doNext:^(NSString *letter) {
    NSLog(@"%@", letter);
}] then:^{
    return [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence.signal;
}];
Merging

+merge: 前面在flatten中提到的水龍頭的合并

RACSubject *letters=[RACSubject subject];
RACSubject *numbers=[RACSubject subject];
RACSignal *merged=[RACSignal merge:@[letters,numbers]];

// Outputs: A 1 B C 2
[merged subscribeNext:^(NSString *x){
    NSLog(@"%@",x);
}];

[letters sendNext:@"A"];
[numbers sendNext:@"1"];
[letters sendNext:@"B"];
[letters sendNext:@"C"];
[numbers sendNext:@"2"];  
Combining latest values

+combineLatest:任何時(shí)刻取每個(gè)水龍頭吐出的最新的那個(gè)玻璃球

RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSignal *combined = [RACSignal
combineLatest:@[ letters, numbers ]
reduce:^(NSString *letter, NSString *number) {
    return [letter stringByAppendingString:number];
}];

// Outputs: B1 B2 C2 C3
[combined subscribeNext:^(id x) {
    NSLog(@"%@", x);
}];

[letters sendNext:@"A"];
[letters sendNext:@"B"];
[numbers sendNext:@"1"];
[numbers sendNext:@"2"];
[letters sendNext:@"C"];
[numbers sendNext:@"3"];
Switching

-switchToLatest:取指定的那個(gè)水龍頭的吐出的最新玻璃球

RACSubject *letters=[RACSubject subject];
RACSubject *numbers=[RACSubject subject];
RACSubject *signalOfSignals=[RACSubject subject];

RACSignal *switched=[signalOfSignals switchToLatest];

// Outputs: A B 1 D
[switched subscribeNext:^(NSString *x){
    NSLog(@"%@",x);
}];

[signalOfSignals sendNext:letters];
[letters sendNext:@"A"];
[letters sendNext:@"B"];

[signalOfSignals sendNext:numbers];
[letters sendNext:@"C"];
[numbers sendNext:@"1"];

[signalOfSignals sendNext:letters];
[numbers sendNext:@"2"];
[letters sendNext:@"D"];
常用宏

RAC 可以看作某個(gè)屬性的值與一些信號(hào)的聯(lián)動(dòng)

RAC(self.submitButton.enabled) = [RACSignal combineLatest:@[self.usernameField.rac_textSignal, self.passwordField.rac_textSignal]reduce:^id(NSString *userName, NSString *password) {
    return @(userName.length &gt;= 6 &amp;&amp; password.length &gt;= 6);
}];
RACObserve 監(jiān)聽屬性的改變,使用block的KVO
[RACObserve(self.textField,text)subscribeNext:^(NSString *newName){
    NSLog(@"%@",newName);
}];
UI Event

RAC為系統(tǒng)UI提供了很多category或渤,非常棒系冗,比如UITextView、UITextField文本框的改動(dòng)rac_textSignal薪鹦,UIButton的的按下rac_command等等掌敬。

常用的模式

map + switchToLatest

switchToLatest: 的作用是自動(dòng)切換signal of signals到最后一個(gè),比如的command.executionSignals就可以使用switchToLatest:池磁。

map:的作用很簡(jiǎn)單奔害,對(duì)sendNextvalue做一下處理,返回一個(gè)新的值地熄。

如果把這兩個(gè)結(jié)合起來就有意思了华临,想象這么個(gè)場(chǎng)景,當(dāng)用戶在搜索框輸入文字時(shí)端考,需要通過網(wǎng)絡(luò)請(qǐng)求返回相應(yīng)的hints雅潭,每當(dāng)文字有變動(dòng)時(shí),需要取消上一次的請(qǐng)求却特,就可以使用這個(gè)配搭扶供。簡(jiǎn)單演示一下:

NSArray *pins = @[@172230988, @172230947, @172230899, @172230777, @172230707]; 
__block NSInteger index = 0; 

RACSignal *signal = [[[[RACSignal interval:0.1 onScheduler:[RACScheduler scheduler]] 
                    take:pins.count] 
                    map:^id(id value) { 
                        return [[[HBAPIManager sharedManager] fetchPinWithPinID:[pins[index++] intValue]] doNext:^(id x) { 
                            NSLog(@"這里只會(huì)執(zhí)行一次"); 
                        }]; 
                    }] 
                    switchToLatest]; 

[signal subscribeNext:^(HBPin *pin) { 
    NSLog(@"pinID:%d", pin.pinID); 
} completed:^{ 
    NSLog(@"completed"); 
}]; 

// output 
// 2014-06-05 17:40:49.851 這里只會(huì)執(zhí)行一次 
// 2014-06-05 17:40:49.851 pinID:172230707 
// 2014-06-05 17:40:49.851 completed 
takeUntil

takeUntil:someSignal 的作用是當(dāng)someSignal sendNext時(shí),當(dāng)前的signal就sendCompleted裂明,someSignal就像一個(gè)拳擊裁判椿浓,哨聲響起就意味著比賽終止。

它的常用場(chǎng)景之一是處理cell的button的點(diǎn)擊事件,比如點(diǎn)擊Cell的詳情按鈕轰绵,需要push一個(gè)VC粉寞,就可以這樣:

[[[cell.detailButton 
rac_signalForControlEvents:UIControlEventTouchUpInside] 
takeUntil:cell.rac_prepareForReuseSignal] 
subscribeNext:^(id x) { 
    // generate and push ViewController 
}]; 

如果不加takeUntil:cell.rac_prepareForReuseSignal,那么每次Cell被重用時(shí)左腔,該button都會(huì)被addTarget:selector唧垦。

替換Delegate

出現(xiàn)這種需求,通常是因?yàn)樾枰獙?duì)Delegate的多個(gè)方法做統(tǒng)一的處理液样,這時(shí)就可以造一個(gè)signal出來振亮,每次該Delegate的某些方法被觸發(fā)時(shí),該signal就會(huì)sendNext鞭莽。

@implementation UISearchDisplayController (RAC) 

- (RACSignal *)rac_isActiveSignal { 
    self.delegate = self; 
    RACSignal *signal = objc_getAssociatedObject(self, _cmd); 
    if (signal != nil) return signal; 
 
    /* Create two signals and merge them */ 
    RACSignal *didBeginEditing = [[self rac_signalForSelector:@selector(searchDisplayControllerDidBeginSearch:)  
                                    fromProtocol:@protocol(UISearchDisplayDelegate)] mapReplace:@YES]; 
    RACSignal *didEndEditing = [[self rac_signalForSelector:@selector(searchDisplayControllerDidEndSearch:)  
                                  fromProtocol:@protocol(UISearchDisplayDelegate)] mapReplace:@NO]; 
    signal = [RACSignal merge:@[didBeginEditing, didEndEditing]]; 
 
 
    objc_setAssociatedObject(self, _cmd, signal, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
    return signal; 
} 
@end 
rac_signalForSelector

rac_signalForSelector: 這個(gè)方法會(huì)返回一個(gè)signal坊秸,當(dāng)selector執(zhí)行完時(shí),會(huì)sendNext澎怒,也就是當(dāng)某個(gè)方法調(diào)用完后再額外做一些事情褒搔。用在category會(huì)比較方便,因?yàn)镃ategory重寫父類的方法時(shí)喷面,不能再通過[super XXX]來調(diào)用父類的方法星瘾,當(dāng)然也可以手寫Swizzle來實(shí)現(xiàn),不過有了rac_signalForSelector:就方便多了惧辈。

rac_signalForSelector: fromProtocol:可以直接實(shí)現(xiàn)對(duì)protocol的某個(gè)方法的實(shí)現(xiàn)(聽著有點(diǎn)別扭呢)琳状,比如,我們想實(shí)現(xiàn)UIScrollViewDelegate的某些方法盒齿,可以這么寫:

[[self rac_signalForSelector:@selector(scrollViewDidEndDecelerating:) fromProtocol:@protocol(UIScrollViewDelegate)] subscribeNext:^(RACTuple *tuple) { 
// do something 
}]; 

[[self rac_signalForSelector:@selector(scrollViewDidScroll:) fromProtocol:@protocol(UIScrollViewDelegate)] subscribeNext:^(RACTuple *tuple) { 
// do something 
}]; 

self.scrollView.delegate = nil; 
self.scrollView.delegate = self; 

注意念逞,這里的delegate需要先設(shè)置為nil,再設(shè)置為self边翁,而不能直接設(shè)置為self翎承,如果self已經(jīng)是該scrollView的Delegate的話。

有時(shí)倒彰,我們想對(duì)selector的返回值做一些處理审洞,但很遺憾RAC不支持,如果真的有需要的話待讳,可以使用Aspects

結(jié)語

這是搜集的一些RAC的常用方法芒澜,如果有不全的地方敬請(qǐng)諒解,但愿能帶來一些幫助创淡,有誤的地方也歡迎指正和探討痴晦。

分享一些關(guān)于RAC的文章,寫得都很好

『狀態(tài)』驅(qū)動(dòng)的世界:ReactiveCocoa
『可變』的熱信號(hào) RACSubject
Pull-Driven 的數(shù)據(jù)流 RACSequence
優(yōu)雅的 RACCommand
用于多播的 RACMulticastConnection
RAC 中的雙向數(shù)據(jù)綁定 RACChannel
理解 RACScheduler 的實(shí)現(xiàn)
從代理到 RACSignal
【長(zhǎng)篇高能】ReactiveCocoa 和 MVVM 入門
iOS MVVM+RAC 從框架到實(shí)戰(zhàn)
ReactiveCocoa自述:工作原理和應(yīng)用
美團(tuán)網(wǎng)官方博客之RACSignal的Subscription深入分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市琳彩,隨后出現(xiàn)的幾起案子誊酌,更是在濱河造成了極大的恐慌部凑,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碧浊,死亡現(xiàn)場(chǎng)離奇詭異涂邀,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)箱锐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門比勉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人驹止,你說我怎么就攤上這事浩聋。” “怎么了臊恋?”我有些...
    開封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵衣洁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我抖仅,道長(zhǎng)坊夫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任岸售,我火速辦了婚禮践樱,結(jié)果婚禮上厂画,老公的妹妹穿的比我還像新娘凸丸。我一直安慰自己,他們只是感情好袱院,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開白布屎慢。 她就那樣靜靜地躺著,像睡著了一般忽洛。 火紅的嫁衣襯著肌膚如雪腻惠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天欲虚,我揣著相機(jī)與錄音集灌,去河邊找鬼。 笑死复哆,一個(gè)胖子當(dāng)著我的面吹牛欣喧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播梯找,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼唆阿,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了锈锤?” 一聲冷哼從身側(cè)響起驯鳖,我...
    開封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤闲询,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后浅辙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扭弧,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年记舆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了寄狼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡氨淌,死狀恐怖泊愧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情盛正,我是刑警寧澤删咱,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站豪筝,受9級(jí)特大地震影響痰滋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜续崖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一敲街、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧严望,春花似錦多艇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拨匆,卻和暖如春姆涩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背惭每。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工骨饿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人台腥。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓宏赘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親览爵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子置鼻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

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