『狀態(tài)』驅(qū)動(dòng)的世界:ReactiveCocoa

原文

請(qǐng)您閱讀原文 『狀態(tài)』驅(qū)動(dòng)的世界:ReactiveCocoa,作者 Draven牌柄。聲明:本文只用做RAC相關(guān)知識(shí)點(diǎn)梳理蒋失,不做他用正什。

RAC 設(shè)計(jì)思維

去中心化的方式丑慎,能簡(jiǎn)化整個(gè)系統(tǒng)的構(gòu)造,使得各個(gè)組件只需要關(guān)心狀態(tài)究珊,以及狀態(tài)對(duì)應(yīng)的動(dòng)作薪者;不再需要一個(gè)中樞系統(tǒng)來(lái)組織、管理其它的組件剿涮,并負(fù)責(zé)大多數(shù)的業(yè)務(wù)邏輯言津。這種自底向下的、狀態(tài)驅(qū)動(dòng)的構(gòu)建方式能夠使用多個(gè)較小的組件取试,減少臃腫的中樞出現(xiàn)的可能性悬槽,從而降低系統(tǒng)的復(fù)雜度。

ReactiveCocoa 對(duì)于狀態(tài)的理解與上述瞬浓,將原有的各種設(shè)計(jì)模式初婆,包括代理、Target/Action猿棉、通知中心以及觀察者模式各種『輸入』磅叛,都抽象成了信號(hào)(也可以理解為狀態(tài)流)讓單一的組件能夠?qū)ψ约旱捻憫?yīng)動(dòng)作進(jìn)行控制,簡(jiǎn)化了視圖控制器的負(fù)擔(dān)铺根。

RACSignal 簡(jiǎn)介

RACSignal 其實(shí)是抽象類 RACStream 的子類宪躯,在整個(gè) ReactiveObjc 工程中有另一個(gè)類 RACSequence 也繼承自抽象類 RACStream

RACSignal-Hierachy.png

RACSignal 可以說(shuō)是 ReactiveCocoa 中的核心類乔宿,也是最重要的概念位迂,整個(gè)框架圍繞著 RACSignal 的概念進(jìn)行組織,對(duì) RACSignal 最簡(jiǎn)單的理解就是它表示一連串的狀態(tài):

What-is-RACSignal.png

在狀態(tài)改變時(shí)详瑞,對(duì)應(yīng)的訂閱者 RACSubscriber 就會(huì)收到通知執(zhí)行相應(yīng)的指令掂林,在 ReactiveCocoa 的世界中所有的消息都是通過(guò)信號(hào)的方式來(lái)傳遞的,原有的設(shè)計(jì)模式都會(huì)簡(jiǎn)化為一種模型坝橡。

RACStream

RACStream 作為抽象類本身不提供方法的實(shí)現(xiàn)泻帮,其實(shí)現(xiàn)內(nèi)部原生提供的而方法都是抽象方法,會(huì)在調(diào)用時(shí)直接拋出異常:

+ (__kindof RACStream *)empty {
    NSString *reason = [NSString stringWithFormat:@"%@ must be overridden by subclasses", NSStringFromSelector(_cmd)];
    @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:reason userInfo:nil];
}

- (__kindof RACStream *)bind:(RACStreamBindBlock (^)(void))block;
+ (__kindof RACStream *)return:(id)value;
- (__kindof RACStream *)concat:(RACStream *)stream;
- (__kindof RACStream *)zipWith:(RACStream *)stream;

RACStream-AbstractMethod

上面的這些抽象方法都需要子類覆寫计寇,不過(guò) RACStreamOperations 分類中使用上面的抽象方法提供了豐富的內(nèi)容锣杂,比如說(shuō) -flattenMap: 方法:

- (__kindof RACStream *)flattenMap:(__kindof RACStream * (^)(id value))block {
    Class class = self.class;

    return [[self bind:^{
        return ^(id value, BOOL *stop) {
            id stream = block(value) ?: [class empty];
            NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);

            return stream;
        };
    }] setNameWithFormat:@"[%@] -flattenMap:", self.name];
}

其他方法比如-skip:脂倦、-take:、-ignore:等等實(shí)例方法都構(gòu)建在這些抽象方法之上元莫,只要子類覆寫了所有抽象方法就能自動(dòng)獲得所有的 Operation 分類中的方法赖阻。

RACStream-Operation

信號(hào)的創(chuàng)建過(guò)程十分簡(jiǎn)單,-createSignal: 是推薦的創(chuàng)建信號(hào)的方法踱蠢,方法其實(shí)只做了一次轉(zhuǎn)發(fā):

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

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    RACDynamicSignal *signal = [[self alloc] init];
    signal->_didSubscribe = [didSubscribe copy];
    return [signal setNameWithFormat:@"+createSignal:"];
}

該方法其實(shí)只是創(chuàng)建了一個(gè) RACDynamicSignal 實(shí)例并保存了傳入的 didSubscribe 代碼塊火欧,在每次有訂閱者訂閱當(dāng)前信號(hào)時(shí),都會(huì)執(zhí)行一遍茎截,向訂閱者發(fā)送消息苇侵。

RACSignal 類簇

雖然 -createSignal: 的方法簽名上返回的是 RACSignal 對(duì)象的實(shí)例,但是實(shí)際上這里返回的是 RACDynamicSignal企锌,也就是 RACSignal 的子類榆浓;同樣,在 ReactiveCocoa 中也有很多其他的 RACSignal 子類霎俩。

使用類簇的方式設(shè)計(jì)的 RACSignal 在創(chuàng)建實(shí)例時(shí)可能會(huì)返回 RACDynamicSignal哀军、RACEmptySignalRACErrorSignalRACReturnSignal 對(duì)象:

RACSignal-Subclasses

其實(shí)這幾種子類并沒(méi)有對(duì)原有的 RACSignal 做出太大的改變打却,它們的創(chuàng)建過(guò)程也不是特別的復(fù)雜杉适,只需要調(diào)用 RACSignal 不同的類方法:

RACSignal-Instantiate-Object

RACSignal 只是起到了一個(gè)代理的作用,最后的實(shí)現(xiàn)過(guò)程還是會(huì)指向?qū)?yīng)的子類:

+ (RACSignal *)error:(NSError *)error {
    return [RACErrorSignal error:error];
}

+ (RACSignal *)empty {
    return [RACEmptySignal empty];
}

+ (RACSignal *)return:(id)value {
    return [RACReturnSignal return:value];
}

RAC 信號(hào)訂閱流程

RACSignal-Subcribe-Process

創(chuàng)建信號(hào) RACSignal 柳击,RACSignal 調(diào)用 -subscribeNext: 方法返回一個(gè) RACDisposable猿推,在訂閱過(guò)程中生成了一個(gè) RACSubscriber 對(duì)象,向這個(gè)對(duì)象發(fā)送消息 -sendNext: 時(shí)捌肴,就會(huì)向所有的訂閱者發(fā)送消息蹬叭。

信號(hào)的訂閱

RACSignal-Subscribe-Methods.png

訂閱者可以選擇自己想要感興趣的信息類型 next/error/completed 進(jìn)行關(guān)注,并在對(duì)應(yīng)的信息發(fā)生時(shí)調(diào)用 block 進(jìn)行處理回調(diào)状知。

所有的方法其實(shí)只是對(duì) nextBlock秽五、completedBlock 以及 errorBlock 的組合,這里以其中最長(zhǎng)的 -subscribeNext:error:completed: 方法的實(shí)現(xiàn)為例(也只需要介紹這一個(gè)方法):

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

方法中傳入的所有 block 參數(shù)都應(yīng)該是非空的饥悴。
拿到了傳入的 block 之后坦喘,使用 +subscriberWithNext:error:completed: 初始化一個(gè) RACSubscriber 對(duì)象的實(shí)例:

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

在拿到這個(gè)對(duì)象之后,調(diào)用 RACSignal-subscribe: 方法傳入訂閱者對(duì)象:

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

RACSignal 類中其實(shí)并沒(méi)有實(shí)現(xiàn)這個(gè)實(shí)例方法西设,需要在上文提到的四個(gè)子類對(duì)這個(gè)方法進(jìn)行覆寫瓣铣,這里僅分析 RACDynamicSignal 中的方法:

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
    subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];

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

    [disposable addDisposable:schedulingDisposable];
    
    return disposable;
}

RACPassthroughSubscriber 就像它的名字一樣,只是對(duì)上面創(chuàng)建的訂閱者對(duì)象進(jìn)行簡(jiǎn)單的包裝贷揽,將所有的消息轉(zhuǎn)發(fā)給內(nèi)部的 innerSubscriber棠笑,也就是傳入的 RACSubscriber 對(duì)象:

- (instancetype)initWithSubscriber:(id<RACSubscriber>)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable {
    self = [super init];

    _innerSubscriber = subscriber;
    _signal = signal;
    _disposable = disposable;

    [self.innerSubscriber didSubscribeWithDisposable:self.disposable];
    return self;
}

如果直接簡(jiǎn)化 -subscribe:方法的實(shí)現(xiàn),你可以看到一個(gè)看起來(lái)極為敷衍的代碼:

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    return self.didSubscribe(subscriber);
}

總而言之禽绪,信號(hào)的訂閱過(guò)程就是初始化 RACSubscriber 對(duì)象蓖救,然后執(zhí)行 didSubscribe 代碼塊的過(guò)程生成 disposable洪规。

Principle-of-Subscribing-Signals

信息的發(fā)送

在 RACSignalBindBlock 中,訂閱者可以根據(jù)自己的興趣選擇自己想要訂閱哪種消息循捺;我們也可以按需發(fā)送三種消息:


RACSignal-Subscription-Messages-Sending

訂閱的回收過(guò)程

在創(chuàng)建信號(hào)時(shí)淹冰,我們向 -createSignal:方法中傳入了 didSubscribe 信號(hào),這個(gè) block 在執(zhí)行結(jié)束時(shí)會(huì)返回一個(gè) RACDisposable 對(duì)象巨柒,用于在訂閱結(jié)束時(shí)進(jìn)行必要的清理樱拴,同樣也可以用于取消因?yàn)橛嗛唲?chuàng)建的正在執(zhí)行的任務(wù)。

而處理這些事情的核心類就是 RACDisposable 以及它的子類:

RACDisposable-And-Subclasses

這篇文章中主要關(guān)注的是左側(cè)的三個(gè)子類洋满,當(dāng)然 RACDisposable 的子類不止這三個(gè)晶乔,還有用于處理 KVO 的 RACKVOTrampoline,不過(guò)在這里我們不會(huì)討論這個(gè)類的實(shí)現(xiàn)牺勾。

RACDisposable

在繼續(xù)分析討論訂閱的回收過(guò)程之前正罢,筆者想先對(duì) RACDisposable 進(jìn)行簡(jiǎn)要的剖析和介紹:

RACDisposable.png

RACDisposable 是以 _disposeBlock 為核心進(jìn)行組織的,幾乎所有的方法以及屬性其實(shí)都是對(duì) _disposeBlock 進(jìn)行的操作驻民。

關(guān)于 _disposeBlock 中的 self

這一小節(jié)的內(nèi)容是可選的翻具,跳過(guò)不影響整篇文章閱讀的連貫性。

_disposeBlock 是一個(gè)私有的指針變量回还,當(dāng) void (^)(void) 類型的 block 被傳入之后都會(huì)轉(zhuǎn)換成 CoreFoundation 中的類型并以 void * 的形式存入 _disposeBlock 中:

+ (instancetype)disposableWithBlock:(void (^)(void))block {
    return [[self alloc] initWithBlock:block];
}

- (instancetype)initWithBlock:(void (^)(void))block {
    self = [super init];

    _disposeBlock = (void *)CFBridgingRetain([block copy]); 
    OSMemoryBarrier();

    return self;
}

奇怪的是裆泳,_disposeBlock 中不止會(huì)存儲(chǔ)代碼塊 block,還有可能存儲(chǔ)橋接之后的self

- (instancetype)init {
    self = [super init];

    _disposeBlock = (__bridge void *)self;
    OSMemoryBarrier();

    return self;
}

這里柠硕,剛開(kāi)始看到可能會(huì)覺(jué)得比較奇怪工禾,有兩個(gè)疑問(wèn)需要解決:

  • 為什么要提供一個(gè) -init 方法來(lái)初始化 RACDisposable 對(duì)象?
  • 為什么要向_disposeBlock 中傳入當(dāng)前對(duì)象蝗柔?

對(duì)于 RACDisposable 來(lái)說(shuō)闻葵,雖然一個(gè)不包含 _disposeBlock 的對(duì)象沒(méi)什么太多的意義,但是對(duì)于 RACSerialDisposable 等子類來(lái)說(shuō)癣丧,卻不完全是這樣槽畔,因?yàn)?RACSerialDisposable-dispose 時(shí),并不需要執(zhí)行 disposeBlock胁编,這樣就浪費(fèi)了內(nèi)存和 CPU 時(shí)間厢钧;但是同時(shí)我們需要一個(gè)合理的方法準(zhǔn)確地判斷當(dāng)前對(duì)象的 isDisposed

所以,使用向 _disposeBlock 中傳入 NULL 的方式來(lái)判斷 isDisposed掏呼;在 -init 調(diào)用時(shí)傳入 self 而不是 NULL 防止?fàn)顟B(tài)被誤判坏快,這樣就在不引入其他實(shí)例變量铅檩、增加對(duì)象的設(shè)計(jì)復(fù)雜度的同時(shí)憎夷,解決了這兩個(gè)問(wèn)題。

如果仍然不理解上述的兩個(gè)問(wèn)題昧旨,在這里舉一個(gè)錯(cuò)誤的例子拾给,如果 _disposeBlock 在使用時(shí)只傳入 NULL 或者 block祥得,那么在 RACCompoundDisposable 初始化時(shí),是應(yīng)該向 _disposeBlock 中傳入什么呢蒋得?

  • 傳入 NULL 會(huì)導(dǎo)致在初始化之后 isDisposed == YES级及,然而當(dāng)前對(duì)象根本沒(méi)有被回收;
  • 傳入 block 會(huì)導(dǎo)致無(wú)用的 block 的執(zhí)行额衙,浪費(fèi)內(nèi)存以及 CPU 時(shí)間饮焦;

這也就是為什么要引入 self 來(lái)作為 _disposeBlock 內(nèi)容的原因。

-dispose: 方法的實(shí)現(xiàn)

這個(gè)只有不到 20 行的 -dispose: 方法已經(jīng)是整個(gè) RACDisposable 類中最復(fù)雜的方法了:

- (void)dispose {
    void (^disposeBlock)(void) = NULL;

    while (YES) {
        void *blockPtr = _disposeBlock;
        if (OSAtomicCompareAndSwapPtrBarrier(blockPtr, NULL, &_disposeBlock)) {
            if (blockPtr != (__bridge void *)self) {
                disposeBlock = CFBridgingRelease(blockPtr);
            }

            break;
        }
    }

    if (disposeBlock != nil) disposeBlock();
}

RACSerialDisposable

RACSerialDisposable 是一個(gè)用于持有 RACDisposable 的容器窍侧,它一次只能持有一個(gè)RACDisposable 的實(shí)例县踢,并可以原子地?fù)Q出容器中保存的對(duì)象:

- (RACDisposable *)swapInDisposable:(RACDisposable *)newDisposable {
    RACDisposable *existingDisposable;
    BOOL alreadyDisposed;

    pthread_mutex_lock(&_mutex);
    alreadyDisposed = _disposed;
    if (!alreadyDisposed) {
        existingDisposable = _disposable;
        _disposable = newDisposable;
    }
    pthread_mutex_unlock(&_mutex);

    if (alreadyDisposed) {
        [newDisposable dispose];
        return nil;
    }

    return existingDisposable;
}

線程安全的 RACSerialDisposable 使用 pthred_mutex_t 互斥鎖來(lái)保證在訪問(wèn)關(guān)鍵變量時(shí)不會(huì)出現(xiàn)線程競(jìng)爭(zhēng)問(wèn)題。
-dispose 方法的處理也十分簡(jiǎn)單:

- (void)dispose {
    RACDisposable *existingDisposable;

    pthread_mutex_lock(&_mutex);
    if (!_disposed) {
        existingDisposable = _disposable;
        _disposed = YES;
        _disposable = nil;
    }
    pthread_mutex_unlock(&_mutex);
    
    [existingDisposable dispose];
}

使用鎖保證線程安全伟件,并在內(nèi)部的 _disposable 換出之后在執(zhí)行 -dispose 方法對(duì)訂閱進(jìn)行處理硼啤。

RACCompoundDisposable

RACSerialDisposable 只負(fù)責(zé)一個(gè) RACDisposable 對(duì)象的釋放不同;RACCompoundDisposable 同時(shí)負(fù)責(zé)多個(gè) RACDisposable 對(duì)象的釋放斧账。

相比于只管理一個(gè) RACDisposable 對(duì)象的 RACSerialDisposable谴返,RACCompoundDisposable 由于管理多個(gè)對(duì)象,其實(shí)現(xiàn)更加復(fù)雜咧织,而且為了性能和內(nèi)存占用之間的權(quán)衡嗓袱,其實(shí)現(xiàn)方式是通過(guò)持有兩個(gè)實(shí)例變量:

@interface RACCompoundDisposable () {
    ...
    RACDisposable *_inlineDisposables[RACCompoundDisposableInlineCount];

    CFMutableArrayRef _disposables;
    ...
}

在對(duì)象持有的 RACDisposable 不超過(guò) RACCompoundDisposableInlineCount 時(shí),都會(huì)存儲(chǔ)在_inlineDisposables 數(shù)組中习绢,而更多的實(shí)例都會(huì)存儲(chǔ)在 _disposables 中:

RACCompoundDisposable

RACCompoundDisposable 在使用 -initWithDisposables:初始化時(shí)索抓,會(huì)初始化兩個(gè) RACDisposable 的位置用于加速銷毀訂閱的過(guò)程,同時(shí)為了不浪費(fèi)內(nèi)存空間毯炮,在默認(rèn)情況下只占用兩個(gè)位置:

- (instancetype)initWithDisposables:(NSArray *)otherDisposables {
    self = [self init];

    [otherDisposables enumerateObjectsUsingBlock:^(RACDisposable *disposable, NSUInteger index, BOOL *stop) {
        self->_inlineDisposables[index] = disposable;
        if (index == RACCompoundDisposableInlineCount - 1) *stop = YES;
    }];

    if (otherDisposables.count > RACCompoundDisposableInlineCount) {
        _disposables = RACCreateDisposablesArray();

        CFRange range = CFRangeMake(RACCompoundDisposableInlineCount, (CFIndex)otherDisposables.count - RACCompoundDisposableInlineCount);
        CFArrayAppendArray(_disposables, (__bridge CFArrayRef)otherDisposables, range);
    }

    return self;
}

如果傳入的 otherDisposables 多于 RACCompoundDisposableInlineCount逼肯,就會(huì)創(chuàng)建一個(gè)新的 CFMutableArrayRef 引用,并將剩余的 RACDisposable 全部傳入這個(gè)數(shù)組中桃煎。

RACCompoundDisposable 中另一個(gè)值得注意的方法就是 -addDisposable:

- (void)addDisposable:(RACDisposable *)disposable {
    if (disposable == nil || disposable.disposed) return;

    BOOL shouldDispose = NO;

    pthread_mutex_lock(&_mutex);
    {
        if (_disposed) {
            shouldDispose = YES;
        } else {
            for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) {
                if (_inlineDisposables[i] == nil) {
                    _inlineDisposables[i] = disposable;
                    goto foundSlot;
                }
            }

            if (_disposables == NULL) _disposables = RACCreateDisposablesArray();
            CFArrayAppendValue(_disposables, (__bridge void *)disposable);
        foundSlot:;
        }
    }
    pthread_mutex_unlock(&_mutex);
    if (shouldDispose) [disposable dispose];
}

在向 RACCompoundDisposable 中添加新的 RACDisposable 對(duì)象時(shí)篮幢,會(huì)先嘗試在 _inlineDisposables 數(shù)組中尋找空閑的位置,如果沒(méi)有找到为迈,就會(huì)加入到 _disposables 中三椿;但是,在添加 RACDisposable 的過(guò)程中也難免遇到當(dāng)前 RACCompoundDisposable 已經(jīng) dispose 的情況葫辐,而這時(shí)就會(huì)直接-dispose` 剛剛加入的對(duì)象搜锰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市耿战,隨后出現(xiàn)的幾起案子蛋叼,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狈涮,死亡現(xiàn)場(chǎng)離奇詭異狐胎,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)歌馍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門握巢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人松却,你說(shuō)我怎么就攤上這事暴浦。” “怎么了晓锻?”我有些...
    開(kāi)封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵肉渴,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我带射,道長(zhǎng)同规,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任窟社,我火速辦了婚禮券勺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘灿里。我一直安慰自己关炼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布匣吊。 她就那樣靜靜地躺著儒拂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪色鸳。 梳的紋絲不亂的頭發(fā)上社痛,一...
    開(kāi)封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天,我揣著相機(jī)與錄音命雀,去河邊找鬼蒜哀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吏砂,可吹牛的內(nèi)容都是我干的撵儿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼狐血,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼淀歇!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起匈织,我...
    開(kāi)封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤浪默,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體浴鸿,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年弦追,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了岳链。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡劲件,死狀恐怖掸哑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情零远,我是刑警寧澤苗分,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站牵辣,受9級(jí)特大地震影響摔癣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纬向,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一择浊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逾条,春花似錦琢岩、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至吃警,卻和暖如春糕篇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背酌心。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工娩缰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谒府。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓拼坎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親完疫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子泰鸡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359