ReactiveCocoa基礎

首先來了解一下函數(shù)式反應型編程Functional Reactive Programming

函數(shù)式反應型編程是兩個聲明式編程的子范例(函數(shù)式+反應式)的組合

函數(shù)式編程

編程范式(Programming paradigm)分類

其實就是計算機編程所使用的方法什猖,是設計程序結構所采用的設計風格盏阶。

目前主流的編程范式有:

  • 命令式編程(Imperative programming)
    • 如Pascal统阿,C語言
    • First DO THIS and next DO THAT
    • 其他統(tǒng)稱為聲明式編程(Declarative Programming)
  • 函數(shù)式編程(Functional programming)
    • 如Haskell,Erlang其徙, Lisp
    • Evaluate an expression and use the resulting value for something
  • 面向對象編程(Object-oriented programming)
    • 如Java归苍,C++
    • Send messages between objects to simulate the temporal evolution of a set of real world phenomena
  • 邏輯編程(Logic Programming)
    • 如Prolog吮便,Mercury黔寇,Logtalk
    • Answer a question via search for a solution
Resize icon
Resize icon

函數(shù)式編程特點

外鏈參考:函數(shù)式編程初探

  • 函數(shù)是"第一等公民"。指的是函數(shù)與其他數(shù)據(jù)類型一樣觉壶,處于平等地位脑题,可以賦值給其他變量,也可以作為參數(shù)铜靶,傳入另一個函數(shù)叔遂,或者作為別的函數(shù)的返回值。
  • 只用"表達式"(expression)争剿,不用"語句"(statement)已艰。函數(shù)式編程要求,只使用表達式秒梅,不使用語句旗芬。也就是說舌胶,每一步都是單純的運算捆蜀,而且都有返回值。
  • 沒有"副作用"(side effect)幔嫂。意味著函數(shù)要保持獨立辆它,所有功能就是返回一個新的值,沒有其他行為履恩,尤其是不得修改外部變量的值锰茉。
  • 不修改狀態(tài)
  • 引用透明(Referential transparency)。指的是函數(shù)的運行不依賴于外部變量或"狀態(tài)"切心,只依賴于輸入的參數(shù)飒筑,任何時候只要參數(shù)相同片吊,引用函數(shù)所得到的返回值總是相同的。

高階函數(shù)

函數(shù)式編程的一個關鍵的概念是"高階函數(shù)"协屡。從維基百科的解釋來看俏脊,一個高階函數(shù)需要滿足下面兩個條件:

  • 一個或者多個函數(shù)作為輸入。
  • 有且僅有一個函數(shù)輸出肤晓。

在Objective-c中我們經常使用block作為函數(shù)爷贫。我們不需要跋山涉水地去尋找‘高階函數(shù)’,實際上补憾,Apple為我們提供的Foundation庫中就有漫萄。考慮象下面這么簡單的一個NSNumber 的數(shù)組:

NSArray * array = @[ @(1), @(2), @(3) ];

我們想要枚舉這個數(shù)組的內容盈匾,可以用一個NSArray的高階函數(shù)來實現(xiàn):

[array enumerateObjectsUsingBlock:^(NSNumber *number, NSUInteger idx, BOOL *stop)
{
    NSLog(@"%@",number);
}];

函數(shù)式編程意義

  • 代碼簡潔腾务,開發(fā)快速
  • 接近自然語言,易于理解
  • 更方便的代碼管理
  • 易于"并發(fā)編程"

函數(shù)式編程和遞歸

外鏈參考:[函數(shù)式編程掃盲篇](http://www.cnblogs.com/kym/archive/2011/03/07/1976519.html

遞歸是函數(shù)式編程的一個重要的概念威酒,循環(huán)可以沒有窑睁,但是遞歸對于函數(shù)式編程卻是不可或缺的。

循環(huán)是在描述我們該如何地去解決問題葵孤。

遞歸是在描述這個問題的定義担钮。

考慮經典的斐波那契數(shù)列問題1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …,我們很容易從數(shù)列本身的定義得到一個遞推式:f(n)=f(n-1)+f(n-2)

先看循環(huán)模型:

def Fib(n): 
    a=1 
    b=1 
    n = n - 1 
    while n>0: 
        temp=a 
        a=a+b 
        b=temp 
        n = n-1 
    return b        

遞歸模型:

def Fib(a): 
    if a==0 or a==1: 
        return 1 
    else: 
        return Fib(a-2)+Fib(a-1)

尾遞歸模型:

def Fib(a,b,n): 
    if n==0: 
        return b 
    else: 
        return Fib(b,a+b,n-1)

模擬

     a, b, n    
Fib (1, 1, 10)
Fib (1, 2, 9)
Fib (2, 3, 8)

什么是尾遞歸尤仍,用最通俗的話說:就是在最后一部單純地去調用遞歸函數(shù)箫津,這里我們要注意“單純”這個字眼。

那么我們說下尾遞歸的原理宰啦,其實尾遞歸就是不要保持當前遞歸函數(shù)的狀態(tài)苏遥,而把需要保持的東西全部用參數(shù)給傳到下一個函數(shù)里,這樣就可以自動清空本次調用的椛哪#空間田炭。這樣的話,占用的椑旄蹋空間就是常數(shù)階的了教硫。

在看尾遞歸代碼之前,我們還是先來明確一下遞歸的分類辆布,我們將遞歸分成“樹形遞歸”和“尾遞歸”瞬矩,什么是樹形遞歸,就是把計算過程逐一展開锋玲,最后形成的是一棵樹狀的結構景用,比如之前的斐波那契數(shù)列的遞歸解法。

響應式編程

看下面一段代碼

a = 2
b = 2
c = a + b // c 是 4
b = 3     // 現(xiàn)在c是多少?

在命令式編程語言中惭蹂,c = a + b這個語句一旦執(zhí)行完畢伞插,a 和 b 再發(fā)生變化就和 c 無關了割粮,c 并不會跟著變化。如果需要 c 變化時媚污,我們一般會封裝一個類似 update_c() 這樣的函數(shù)穆刻,在 a 或 b 變化的時候調用一下,來更新c杠步。

而在響應式編程的思想中氢伟,上面的語句實際上是建立了 c 和 a、b 的關聯(lián)關系幽歼,這樣朵锣,當 a 或 b 發(fā)生變化的時候,c 可以自動變化甸私。

Excel就是響應式編程的一個例子诚些。單元格可以包含字面值或類似”=B1+C1″的公式,而包含公式的單元格的值會依據(jù)其他單元格的值的變化而變化 皇型。

注意這只是一個思想诬烹,或者說是目標,我們可以使用個各種語言來實現(xiàn)這個目標弃鸦,并不是說必須要有一種專門的響應型編程語言绞吁。當然,語言可以根據(jù)這個思路來設計唬格,會讓響應型編程的實現(xiàn)更為簡單家破。

kvo, Cocoa Binding,

ReactiveCocoa

外鏈: ReactiveCocoa iOS的函數(shù)響應型編程 ReactiveCocoa for a better world ReactiveCocoa v2.5 源碼解析之架構總覽

ReactiveCocoa是函數(shù)式響應型編程的一個實現(xiàn)。它受 Functional Reactive Programming 的啟發(fā)购岗,是 Justin Spahr-Summers 和 Josh Abernathy 在開發(fā) GitHub for Mac 過程中的一個副產品汰聋,它提供了一系列用來組合和轉換值流的 API 。

ReactiveCocoa 的版本演進歷程喊积,簡單介紹如下:

  • <= v2.5 :Objective-C 烹困;
  • v3.x :Swift 1.2 ;
  • v4.x :Swift 2.x 乾吻。

本文所介紹的均為 ReactiveCocoa v2.5 版本中的內容髓梅,這是 Objective-C 最新的穩(wěn)定版本。

ReactiveCocoa類圖如下圖所示

!
Resize icon

ReactiveCocoa 主要由以下四大核心組件構成:

  • 流:RACStream 及其子類溶弟;
  • 訂閱者:RACSubscriber 的實現(xiàn)類及其子類女淑;
  • 調度器:RACScheduler 及其子類瞭郑;
  • 清潔工:RACDisposable 及其子類辜御。

RACStream

在ReactiveCocoa中,流RACStream代表的是隨著時間而改變的值流(Streams of values over time)屈张。
你可以把它想象成水龍頭中的水擒权,當你打開水龍頭時袱巨,水源源不斷地流出來;你也可以把它想象成電碳抄,當你插上插頭時愉老,電靜靜地充到你的手機上;你還可以把它想象成運送玻璃珠的管道剖效,當你打開閥門時嫉入,珠子一個接一個地到達。這里的水璧尸、電咒林、玻璃珠就是我們所需要的值,而打開水龍頭爷光、插上插頭垫竞、打開閥門就是訂閱它們的過程。

RACStream 是一個抽象類蛀序,通常情況下欢瞪,我們并不會去實例化它,而是直接使用它的兩個子類信號RACSignal和序列RACSequence徐裸。

信號RACSignal

RACSignal代表的是未來將會被傳送的值遣鼓,它是一種push-driven的流。

信號又是最核心的部分重贺,其他組件都是圍繞它運作的譬正。

對于一個應用來說,絕大部分的時間都是在等待某些事件的發(fā)生或響應某些狀態(tài)的變化檬姥,比如用戶的觸摸事件曾我、應用進入后臺、網絡請求成功刷新界面等等健民,而維護這些狀態(tài)的變化抒巢,常常會使代碼變得非常復雜,難以擴展秉犹。而 ReactiveCocoa 給出了一種非常好的解決方案蛉谜,它使用信號來代表這些異步事件,提供了一種統(tǒng)一的方式來處理所有異步的行為崇堵,包括代理方法型诚、block 回調、target-action 機制鸳劳、通知狰贯、KVO 等:

// 代理方法
[[self
    rac_signalForSelector:@selector(webViewDidStartLoad:)
    fromProtocol:@protocol(UIWebViewDelegate)]
    subscribeNext:^(id x) {
        // 實現(xiàn) webViewDidStartLoad: 代理方法
}];

// target-action
[[self.avatarButton
    rac_signalForControlEvents:UIControlEventTouchUpInside]
    subscribeNext:^(UIButton *avatarButton) {
        // avatarButton 被點擊了
}];

// 通知
[[[NSNotificationCenter defaultCenter]
    rac_addObserverForName:kReachabilityChangedNotification object:nil]
    subscribeNext:^(NSNotification *notification) {
        // 收到 kReachabilityChangedNotification 通知
}];

// KVO
[RACObserve(self, username) subscribeNext:^(NSString *username) {
    // 用戶名發(fā)生了變化
}];

然而,這些還只是 ReactiveCocoa 的冰山一角,它真正強大的地方在于我們可以對這些不同的信號進行任意地組合和鏈式操作涵紊,從最原始的輸入 input 開始直至得到最終的輸出 output 為止:

[[[RACSignal
    combineLatest:@[ RACObserve(self, username), RACObserve(self, password) ]
    reduce:^(NSString *username, NSString *password) {
    return @(username.length > 0 && password.length > 0);
    }]
    distinctUntilChanged]
    subscribeNext:^(NSNumber *valid) {
        if (valid.boolValue) {
            // 用戶名和密碼合法傍妒,登錄按鈕可用
        } else {
            // 用戶名或密碼不合法,登錄按鈕不可用
        }
}];

因此摸柄,對于 ReactiveCocoa 來說颤练,我們可以毫不夸張地說,阻礙它發(fā)揮的瓶頸就只剩下你的想象力了驱负。

RACSignal可以向訂閱者發(fā)送三種不同類型的事件:

  • nextRACSignal通過next事件向訂閱者傳送新的值嗦玖,并且這個值可以為 nil ;
  • errorRACSignal通過error事件向訂閱者表明信號在正常結束前發(fā)生了錯誤跃脊;
  • completedRACSignal通過completed事件向訂閱者表明信號已經正常結束踏揣,不會再有后續(xù)的值傳送給訂閱者。

注意匾乓,ReactiveCocoa 中的值流只包含正常的值捞稿,即通過next事件傳送的值,并不包括errorcompleted事件拼缝,它們需要被特殊處理娱局。通常情況下,一個信號的生命周期是由任意個next事件和一個error事件或一個completed事件組成的咧七。

從前面的類圖中衰齐,我們可以看出,RACSignal并非只有一個類继阻,事實上耻涛,它的一系列功能是通過類簇來實現(xiàn)的。除去我們將在下節(jié)介紹的 RACSubject及其子類外瘟檩,RACSignal還有五個用來實現(xiàn)不同功能的私有子類:

  • RACEmptySignal:空信號抹缕,用來實現(xiàn) RACSignal+empty方法;
  • RACReturnSignal:一元信號墨辛,用來實現(xiàn) RACSignal+return:方法卓研;
  • RACDynamicSignal:動態(tài)信號,使用一個block來實現(xiàn)訂閱行為睹簇,我們在使用RACSignal+createSignal:方法時創(chuàng)建的就是該類的實例奏赘;
  • RACErrorSignal:錯誤信號,用來實現(xiàn)RACSignal+error:方法太惠;
  • RACChannelTerminal:通道終端磨淌,代表RACChannel的一個終端,用來實現(xiàn)雙向綁定凿渊。

對于RACSignal類簇來說梁只,最核心的方法莫過于-subscribe:了缚柳,這個方法封裝了訂閱者對信號源的一次訂閱過程,它是訂閱者與信號源產生聯(lián)系的唯一入口敛纲。因此,對于RACSignal的所有子類來說剂癌,這個方法的實現(xiàn)邏輯就代表了該子類的具體訂閱行為淤翔,是區(qū)分不同子類的關鍵所在。同時佩谷,這也是為什么RACSignal中的-subscribe:方法是一個抽象方法旁壮,并且必須要讓子類實現(xiàn)的原因:

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCAssert(NO, @"This method must be overridden by subclasses");
    return nil;
}

序列RACSequence

RACSequence代表的是一個不可變的值的序列,與RACSignal不同谐檀,它是pull-driven類型的流抡谐。從嚴格意義上講,RACSequence并不能算作是信號源桐猬,因為它并不能像RACSignal那樣麦撵,可以被訂閱者訂閱,但是它與RACSignal之間可以非常方便地進行轉換溃肪。序列提供了Foundation沒有的一些高階函數(shù)如map, filter, fold等免胃。

使用rac_sequeuece我們能夠輕松地將數(shù)組轉化為一個序列:

NSArray *array = @[ @1, @2, @3 ];
RACSequence * stream = [array rac_sequence];

我們可以將流應用在平方數(shù)映射上,然后轉化回一個數(shù)組:

[stream map:^id (id value){
    return @(pow([value integerValue], 2));
}];
NSLog(@"%@",[stream array]);

當然惫撰,我們可以合并上面的方法調用來避免污染變量的作用域.

NSLog(@"%@",[[[array rac_sequence] map:^id (id value){
                return @(pow([value integerValue], 2));
            }] array]);

我們來看一下filtering羔沙。為了使用ReactiveCocoa來過濾我們的數(shù)組,我們需要再一次把它序列化以便于使用過濾厨钻。

NSLog(@"%@", [[[array rac_sequence] filter:^BOOL (id value){
                    return [value integerValue] % 2 == 0;
                }] array]);

最后看一下怎么讓一個序列流合并為單個值(folding):

NSLog(@"%@",[[[array rac_sequence] map:^id (id value){
                return [value stringValue];
            }] foldLeftWithStart:@"" reduce:^id (id accumulator, id value){
                return [accumulator stringByAppendingString:value];
        }]);

因此扼雏,我們可以非常方便地使用 RACSequence 來實現(xiàn)集合的鏈式操作,直到得到你想要的最終結果為止夯膀。除此之外诗充,使用 RACSequence 的另外一個主要好處是,RACSequence 中包含的值在默認情況下是懶計算的诱建,即只有在真正用到的時候才會被計算其障,并且只會計算一次。也就是說涂佃,如果我們只用到了一個 RACSequence 中的部分值的時候励翼,它就在不知不覺中提高了我們應用的性能。

同樣的辜荠,RACSequence 的一系列功能也是通過類簇來實現(xiàn)的汽抚,它共有九個用來實現(xiàn)不同功能的私有子類。RACSequence 為類簇提供了統(tǒng)一的對外接口伯病,對于使用它的客戶端代碼來說造烁,完全不需要知道私有子類的存在否过,很好地隱藏了實現(xiàn)細節(jié)。另外惭蟋,值得一提的是苗桂,RACSequence 實現(xiàn)了快速枚舉的協(xié)議 NSFastEnumeration ,在這個協(xié)議中只聲明了一個看上去非常抽筋的方法:

- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len;

有興趣的同學告组,可以看看 RACSequence 中的相關實現(xiàn)崇猫,我們將會在后續(xù)的文章中進行介紹屠缭。因此,我們也可以直接使用 for in 來遍歷一個 RACSequence 。

  • BlockKit類似
  • 看源碼感覺性能不是很好父款,有待驗證峦剔。對于幾個霹陡、幾十個的小數(shù)組皆尔,應該問題不大

訂閱者RACSubscriber

調度器RACScheduler

清潔工RACDisposable

RACCommand

ReactiveCocoa Essentials: Understanding and Using RACCommand

ReactiveCocoa對富途牛牛的一些啟發(fā)

界面控件綁定

將控件emailTextField內容賦值給self.viewModel.email

RAC(self.viewModel, email) = self.emailTextField.rac_textSignal;  

將self.viewModel.statusMessage顯示到statusLabel控件

RAC(self.statusLabel, text) =RACObserve(self.viewModel, statusMessage);  

統(tǒng)一處理所有異步的行為

使用信號來代表這些異步事件,提供了一種統(tǒng)一的方式來處理所有異步的行為矫俺,包括代理方法吱殉、block 回調、target-action 機制厘托、通知考婴、KVO

富途牛牛項目:

  • 解決KVO訂閱忘記取消訂閱的問題
  • 解決訂閱通知忘記取消的問題

鏈式依賴的操作

依賴關系通常出現(xiàn)在網絡請求中,如后一個請求應該等前一個請求完成后再創(chuàng)建,等等:

[client logInWithSuccess:^{
    [client loadCachedMessagesWithSuccess:^(NSArray *messages) {
        [client fetchMessagesAfterMessage:messages.lastObject   success:^(NSArray *nextMessages) {
         NSLog(@"Fetched all messages.");
        } failure:^(NSError *error) {
            [self presentError:error];
        }];
    } failure:^(NSError *error) {
        [self presentError:error];
    }];
} failure:^(NSError *error) {
    [self presentError:error];
}];

ReactiveCocoa 可以特別方便地處理這種邏輯模式:

[[[[client logIn]
    then:^{
        return [client loadCachedMessages];
    }]
    flattenMap:^(NSArray *messages) {
        return [client fetchMessagesAfterMessage:messages.lastObject];
    }]
    subscribeError:^(NSError *error) {
        [self presentError:error];
    } completed:^{
        NSLog(@"Fetched all messages.");
    }];

TCP短連接可以這樣優(yōu)化

[[[[service connect] flattenMap:^RACStream *(id value) {
    return [service doSomething1];
}] flattenMap:^RACStream *(id something1Value) {
    // if doSomething1 is successful,   'somethingValue' is passed via sendNext
    return [service disconnect];
}] subscribeError:^(NSError *error) {
    // Error occurred!  Handle "error" if necessary.
} completed:^{
    // Asynchronous chain of operations succeeded.
}];

在異步操作上使用signals信號,讓通過鏈接和轉換這些signal信號,構建更加復雜的行為成為可能.可以在一組操作完成后,來觸發(fā)此操作即可:

// 執(zhí)行兩個網絡操作,并在它們都完成后在控制臺打印信息.
//
// +merge: 傳入一組signal信號,并返回一個新的RACSignal信號對象.這個新返回的RACSignal信號對象,傳遞所有請求的值,并在所有的請求完成時完成.即:新返回的RACSignal信號,在每個請求完成時,都會發(fā)送個消息;在所有消息完成時,除了發(fā)送消息外,還會觸發(fā)"完成"相關的block.
//
// -subscribeCompleted: signal信號完成時,將會執(zhí)行block.
[[RACSignal 
    merge:@[ [client fetchUserRepos], [client fetchOrgRepos] ]] 
    subscribeCompleted:^{
     NSLog(@"They're both done!");
    }];

富途牛牛項目,將很多異步操作做成獨立的RACSigal, 方便單元測試催烘,方便組織先后順序沥阱、依賴關系。比如優(yōu)化啟動流程伊群、登錄流程考杉。

網絡請求避免過于頻繁

股票網絡搜索

 [[[[[[[[[self.textField.rac_textSignal
        filter:^BOOL(id value) { //過濾
            return [value length] > 0;
        }]
       throttle:0.3] //無任何輸入0.3秒后繼續(xù)
      logAll]
     flattenMap:^id(id value) { //flattenMap有新的搜索請求后,上一次網絡請求結果會被忽略
         return [self search:value];
     }]
    logAll]
   map:^id(NSArray *array) {
       return [[[array rac_sequence] map:^id(id value) {
           Stock *stock = [[Stock alloc] init];
           stock.name = value[@"name"];
           stock.symbol = value[@"symbol"];
           stock.exch = value[@"exchDisp"];
           return stock;
       }] array];
   }]
  logAll]
 deliverOn:[RACScheduler mainThreadScheduler]]
 subscribeNext:^(id x) {
     self.stocks = x;
 } error:^(NSError *error) {
     
 } completed:^{
     
 }];

一些坑

block要記得用strongify/weakify

因為RAC很多操作都是在Block中完成的舰始,這塊最常見的問題就是在block直接把self拿來用崇棠,造成block和self的retain cycle。所以需要通過@strongify和@weakify來消除循環(huán)引用丸卷。

有些地方很容易被忽略枕稀,比如RACObserve(thing, keypath),看上去并沒有引用self谜嫉,所以在subscribeNext時就忘記了weakify/strongify萎坷。但事實上RACObserve總是會引用self,即使target不是self沐兰,所以只要有RACObserve的地方都要使用weakify/strongify哆档。

RACObserve(self, model.title) 與 RACObserve(self.model, title)

// 描述: self 本身有一個title屬性和一個model屬性,model本身也有一個title屬性.
RAC(self, title, @"") = RACObserve(self, model.title);
RAC(self, title, @"") = RACObserve(self.model, title);

這兩行代碼,有著質的不同!

RAC(self, title, @"") = RACObserve(self, model.title);

適用場景: self的model屬性改變時,動態(tài)改變self自身title屬性的值,其值為新model的title屬性.

RAC(self, title, @"") = RACObserve(self.model, title);

適用場景: self的model屬性的title屬性改變時,動態(tài)改變self自身title屬性的值,其值為原有model的title屬性.

參考鏈接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市住闯,隨后出現(xiàn)的幾起案子瓜浸,更是在濱河造成了極大的恐慌澳淑,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件插佛,死亡現(xiàn)場離奇詭異杠巡,居然都是意外死亡,警方通過查閱死者的電腦和手機雇寇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門氢拥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谢床,你說我怎么就攤上這事兄一±逑撸” “怎么了识腿?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長造壮。 經常有香客問我渡讼,道長,這世上最難降的妖魔是什么耳璧? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任成箫,我火速辦了婚禮,結果婚禮上旨枯,老公的妹妹穿的比我還像新娘蹬昌。我一直安慰自己,他們只是感情好攀隔,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布皂贩。 她就那樣靜靜地躺著,像睡著了一般昆汹。 火紅的嫁衣襯著肌膚如雪明刷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天满粗,我揣著相機與錄音辈末,去河邊找鬼。 笑死映皆,一個胖子當著我的面吹牛挤聘,可吹牛的內容都是我干的。 我是一名探鬼主播捅彻,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼檬洞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了沟饥?” 一聲冷哼從身側響起添怔,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤湾戳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后广料,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體砾脑,經...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年艾杏,在試婚紗的時候發(fā)現(xiàn)自己被綠了韧衣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡购桑,死狀恐怖畅铭,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情勃蜘,我是刑警寧澤硕噩,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站缭贡,受9級特大地震影響炉擅,放射性物質發(fā)生泄漏。R本人自食惡果不足惜阳惹,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一谍失、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧莹汤,春花似錦快鱼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至荒勇,卻和暖如春柒莉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背沽翔。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工兢孝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人仅偎。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓跨蟹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親橘沥。 傳聞我的和親對象是個殘疾皇子窗轩,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內容