RxSwift特征序列

一、概述
二吴裤、Single
三旧找、Completable
四、Maybe
五麦牺、Driver
六钮蛛、Signal
七鞭缭、ControlEvent

ReactiveX.png

一、概述

任何序列都可以用Observable描述魏颓,創(chuàng)建序列 -> 訂閱序列 -> 信號(hào)發(fā)送 -> 信號(hào)接收岭辣。

Observable<Any>.create { (observer) -> Disposable in
    observer.onNext("信號(hào)1")
    return Disposables.create()
}.subscribe(onNext: { (val) in
    print("信號(hào)接收區(qū):\(val)")
}).disposed(by: disposeBag)

Observable是通用序列的描述符,調(diào)用.onNext甸饱,.onError沦童,onCompleted來(lái)發(fā)送信號(hào),通用性強(qiáng)柜候,但針對(duì)特殊需求可能會(huì)覺(jué)得繁瑣搞动,因此RxSwift還提供了一組特征序列,是Observable序列的變種渣刷,它能夠幫助我們更準(zhǔn)確的描述序列鹦肿。即SingleCompletable辅柴、Maybe箩溃、DriverSignal碌嘀、ControlEvent涣旨。

二、Single

1股冗、定義

單元素序列霹陡,信號(hào)只發(fā)送一次,響應(yīng)信號(hào)或錯(cuò)誤信號(hào)止状。

Single<Any>.create { (single) -> Disposable in
            single(.success("假裝我是一個(gè)正兒八經(jīng)的數(shù)據(jù)"))
            //single(.error(NSError.init(domain: "網(wǎng)絡(luò)出現(xiàn)錯(cuò)誤", code: 101, userInfo:["name":"hibo"])))
            return Disposables.create()
        }.subscribe(onSuccess: { (val) in
            print("Single:\(val)")
        }) { (error) in
            print("SingleError:\(error)")
        }.disposed(by: disposeBag)
  • sinngle(.success(data)) -> onSuccess 發(fā)送響應(yīng)元素到成功觀察者
  • sinngle(.error(error)) -> error 發(fā)送錯(cuò)誤元素到錯(cuò)誤觀察者

響應(yīng)元素和錯(cuò)誤元素分開(kāi)處理烹棉,此時(shí)我們可以聯(lián)想到應(yīng)用中的網(wǎng)絡(luò)請(qǐng)求,成功數(shù)據(jù)用來(lái)渲染怯疤,錯(cuò)誤數(shù)則據(jù)彈出提示框浆洗。

2、源碼探索

2.1集峦、Single定義

/// Sequence containing exactly 1 element
public enum SingleTrait { }
/// Represents a push style sequence containing 1 element.
public typealias Single<Element> = PrimitiveSequence<SingleTrait, Element>

public enum SingleEvent<Element> {
    /// One and only sequence element is produced. (underlying observable sequence emits: `.next(Element)`, `.completed`)
    case success(Element)
    
    /// Sequence terminated with an error. (underlying observable sequence emits: `.error(Error)`)
    case error(Swift.Error)
}

定位到Single.swift文件伏社,首先能看到SinglePrimitiveSequence結(jié)構(gòu)體類型的別名,SingleEvent是事件枚舉塔淤,有successerror兩個(gè)成員變量摘昌。

2.2、create創(chuàng)建序列高蜂。代碼如下(此處代碼標(biāo)記為1??):

extension PrimitiveSequenceType where TraitType == SingleTrait {
    public typealias SingleObserver = (SingleEvent<ElementType>) -> Void
          //代碼省略若干行
    public static func create(subscribe: @escaping (@escaping SingleObserver) -> Disposable) -> Single<ElementType> {
        let source = Observable<ElementType>.create { observer in
            return subscribe { event in
                switch event {
                case .success(let element):
                    observer.on(.next(element))
                    observer.on(.completed)
                case .error(let error):
                    observer.on(.error(error))
                }
            }
        }
        
        return PrimitiveSequence(raw: source)
    }
}

首先看參數(shù)是一個(gè)帶Disposable類型返回值的閉包第焰,交由外部(業(yè)務(wù)層)實(shí)現(xiàn),內(nèi)部調(diào)用向外傳入一個(gè)SingleObserver閉包妨马,以上寫(xiě)法不太好理解挺举,我們可以換一種寫(xiě)法:

public static func create(subscribe: @escaping (@escaping SingleObserver) -> Disposable) -> Single<ElementType> {
    let source = Observable<ElementType>.create { observer in
        // 1、內(nèi)部實(shí)現(xiàn)一個(gè)閉包烘跺,用來(lái)接收外界傳入的SingleEvent信號(hào)湘纵,接著做進(jìn)一步的信號(hào)發(fā)送
        let block = { (event:SingleEvent<ElementType>) -> Void in
            switch event {
            case .success(let element):
                observer.on(.next(element))
                observer.on(.completed)
            case .error(let error):
                observer.on(.error(error))
            }
        }
        // 2、調(diào)用外部實(shí)現(xiàn)的閉包方法滤淳,向外部發(fā)送內(nèi)部實(shí)現(xiàn)的閉包方法做連接作用
        let disposable = subscribe(block)//3梧喷、返回值Disposable對(duì)象 
        return disposable
    }
    return PrimitiveSequence(raw: source)//4、創(chuàng)建PrimitiveSequence對(duì)象并保存Observable序列對(duì)象
}
  • 內(nèi)部實(shí)現(xiàn)一個(gè)閉包block脖咐,用來(lái)接收外界傳入的SingleEvent信號(hào)铺敌,接著做進(jìn)一步的信號(hào)發(fā)送
  • 調(diào)用外部實(shí)現(xiàn)的閉包方法,將內(nèi)部實(shí)現(xiàn)的閉包block發(fā)送出去屁擅,起連接作用
  • 創(chuàng)建PrimitiveSequence對(duì)象并保存Observable序列對(duì)象source偿凭,返回PrimitiveSequence對(duì)象

create方法內(nèi)部實(shí)際上實(shí)現(xiàn)了一個(gè)Observable序列,由此可見(jiàn)Single序列是對(duì)Observable序列的封裝派歌,Disposable對(duì)象通過(guò)閉包交由業(yè)務(wù)層創(chuàng)建弯囊,Single序列在實(shí)現(xiàn)上,方式方法與Observable保持一致胶果,此處可稱一絕匾嘱。當(dāng)前我們只探索Single的信號(hào)是如何完成傳遞,Observable序列的信號(hào)傳遞流程在《Swift核心源碼探索》中有詳細(xì)介紹早抠。

2.3霎烙、訂閱序列

也是在同PrimitiveSequenceType擴(kuò)展中定義,代碼如下(此處代碼標(biāo)記為2??):

public func subscribe(onSuccess: ((ElementType) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil) -> Disposable {
    #if DEBUG
         let callStack = Hooks.recordCallStackOnError ? Thread.callStackSymbols : []
    #else
        let callStack = [String]()
    #endif

    return self.primitiveSequence.subscribe { event in
        switch event {
        case .success(let element):
            onSuccess?(element)
        case .error(let error):
            if let onError = onError {
                onError(error)
            } else {
                Hooks.defaultErrorHandler(callStack, error)
            }
        }
    }
}

方法中先調(diào)用了self.primitiveSequence方法蕊连,返回了self悬垃,方法是在遵循PrimitiveSequenceType協(xié)議的PrimitiveSequence的擴(kuò)展中,為了保證協(xié)議的一致性咪奖。代碼如下:

extension PrimitiveSequence: PrimitiveSequenceType {
    /// Additional constraints
    public typealias TraitType = Trait
    /// Sequence element type
    public typealias ElementType = Element

    // Converts `self` to primitive sequence.
    ///
    /// - returns: Observable sequence that represents `self`.
    public var primitiveSequence: PrimitiveSequence<TraitType, ElementType> {
        return self
    }
}

緊接著調(diào)用另一個(gè)subscribe方法盗忱,代碼如下(此處代碼標(biāo)記為3??):

public func subscribe(_ observer: @escaping (SingleEvent<ElementType>) -> Void) -> Disposable {
    var stopped = false
    return self.primitiveSequence.asObservable().subscribe { event in
        if stopped { return }
        stopped = true
        
        switch event {
        case .next(let element):
            observer(.success(element))
        case .error(let error):
            observer(.error(error))
        case .completed:
            rxFatalErrorInDebug("Singles can't emit a completion event")
        }
    }
}
  • self.primitiveSequence -> asObservable() -> subscribe
  • 此處截?cái)嗔?code>completed信號(hào)的向上傳遞,因此Single序列只能收到響應(yīng)信號(hào)和錯(cuò)誤信號(hào)

該段代碼也調(diào)用了self.primitiveSequence方法羊赵,接著調(diào)用asObservable()方法趟佃,查看代碼發(fā)現(xiàn)此處是為了獲取source對(duì)象,即Observable可觀察序列昧捷。

再查看subscribe的方法(此處標(biāo)記為代碼4??):

public func subscribe(_ on: @escaping (Event<E>) -> Void)
    -> Disposable {
        let observer = AnonymousObserver { e in
            on(e)
        }
        return self.asObservable().subscribe(observer)
}
  • 代碼創(chuàng)建了一個(gè)觀察者闲昭,當(dāng)前觀察者將會(huì)收到發(fā)送過(guò)來(lái)的消息,并由此通過(guò)閉包一層層傳到業(yè)務(wù)層靡挥。 4?? -> 3?? -> 2?? -> 1?? ->業(yè)務(wù)層

  • 當(dāng)前self指向的是1??處創(chuàng)建并保存的Observable類型的source對(duì)象序矩,因此該處subscribe所調(diào)用的即是Produce類中的subscribe方法,在方法內(nèi)部創(chuàng)建了sink對(duì)象跋破,來(lái)觸發(fā)創(chuàng)建序列時(shí)實(shí)現(xiàn)的閉包簸淀,即代碼1??處所create后的閉包

  • 此時(shí)就到了業(yè)務(wù)層瓶蝴,通過(guò)create內(nèi)部實(shí)現(xiàn)的閉包single向內(nèi)部發(fā)送消息,再有observer調(diào)用on來(lái)向觀察者發(fā)送信號(hào)

  • 信號(hào)發(fā)送不做贅述租幕,最終會(huì)到達(dá)4??處代碼的觀察者舷手,此時(shí)再由閉包一層層向上傳遞,直到業(yè)務(wù)層的監(jiān)聽(tīng)閉包

總結(jié):

序列的產(chǎn)生劲绪,訂閱男窟,發(fā)送,接收還是由Observable來(lái)實(shí)現(xiàn)的贾富,Single只是對(duì)Observable做了封裝歉眷,去除了onCompleted的消息監(jiān)聽(tīng)及消息發(fā)送。

具體的Observable序列產(chǎn)生到觀察流程見(jiàn)《Swift核心源碼探索》

三颤枪、Completable

只能產(chǎn)生completed事件和error事件汗捡,沒(méi)有序列元素值產(chǎn)生。

Completable.create { (completable) -> Disposable in
    completable(.completed)
    //completable(.error(NSError.init(domain: "出現(xiàn)異常", code: 101, userInfo: nil)))
    return Disposables.create()
}.subscribe(onCompleted: {
    print("Completable")
}) { (error) in
    print(error)
}.disposed(by: disposeBag)
  • 應(yīng)用場(chǎng)景汇鞭,只關(guān)心任務(wù)是否完成凉唐,不關(guān)心不需要結(jié)果
  • Competable.swift下,在PrimitiveSequenceType擴(kuò)展中實(shí)現(xiàn)了序列的創(chuàng)建霍骄,訂閱台囱,即信號(hào)轉(zhuǎn)發(fā)

定義如下:

/// Sequence containing 0 elements
public enum CompletableTrait { }
/// Represents a push style sequence containing 0 elements.
public typealias Completable = PrimitiveSequence<CompletableTrait, Swift.Never>

public enum CompletableEvent {
    /// Sequence terminated with an error. (underlying observable sequence emits: `.error(Error)`)
    case error(Swift.Error)
    
    /// Sequence completed successfully.
    case completed
}

同樣Completable類也是PrimitiveSequence的別名,并聲明一個(gè)枚舉包含读整,errorcompleted成員變量簿训,限定了事件產(chǎn)生類型。都是對(duì)Observable序列的封裝米间,源碼此處不做探索說(shuō)明强品,和Single一致,只是在訂閱階段對(duì).next事件做了攔截屈糊。

四的榛、Maybe

Single序列相似,發(fā)出一個(gè)元素或一個(gè)completed事件或error事件逻锐。

Maybe<Any>.create { (maybe) -> Disposable in
        maybe(.success("element"))
        //maybe(.completed)
        //maybe(.error(NSError.init(domain: "出現(xiàn)異常", code: 101, userInfo: nil)))
        return Disposables.create()
    }.subscribe(onSuccess: { (val) in
        print(val)
    }, onError: { (error) in
        print("error:\(error)")
    }) {
        print("completed")
    }.disposed(by: disposeBag)

在開(kāi)發(fā)中夫晌,如果一個(gè)業(yè)務(wù)有時(shí)候需要一個(gè)元素,有時(shí)候只需要知道處理完成的時(shí)候昧诱,可以使用該Maybe晓淀,解決不確定需求問(wèn)題。源碼探索略盏档,同上凶掰。

五、Driver

  • Driver序列不會(huì)產(chǎn)生error事件
  • 在主線程中監(jiān)聽(tīng),會(huì)向新訂閱者發(fā)送上次發(fā)送過(guò)的元素懦窘,簡(jiǎn)化UI層的代碼
  • 共享序列

下面看一下為什么會(huì)擴(kuò)展一個(gè)Driver序列前翎。

有一個(gè)需求:

  • 搜索框中每次輸入一個(gè)文本,獲取一次網(wǎng)絡(luò)請(qǐng)求奶赠,成功后渲染UI鱼填,多個(gè)控件顯示

先實(shí)現(xiàn)一個(gè)簡(jiǎn)單的UI

let tf = UITextField.init(frame: CGRect(x: 100, y: 100, width: 200, height: 40))
tf.borderStyle = .roundedRect
tf.placeholder = "請(qǐng)輸入"
self.view.addSubview(tf)

let label1 = UILabel.init(frame: CGRect(x: 100, y: 160, width: 200, height: 40))
label1.backgroundColor = .groupTableViewBackground
label1.textAlignment = .center
self.view.addSubview(label1)

let label2 = UILabel.init(frame: CGRect(x: 100, y: 210, width: 200, height: 40))
label2.backgroundColor = .groupTableViewBackground
label2.textAlignment = .center
self.view.addSubview(label2)

創(chuàng)建了一個(gè)textfield,兩個(gè)label用來(lái)展示毅戈。下面在來(lái)實(shí)現(xiàn)一個(gè)網(wǎng)絡(luò)請(qǐng)求,返回一個(gè)Single序列:

func network(text:String) -> Single<Any> {
    return Single<Any>.create(subscribe: { (single) -> Disposable in
        if text == "1234"{
            single(.error(NSError.init(domain: "出現(xiàn)錯(cuò)誤", code: 101, userInfo: nil)))
        }
        DispatchQueue.global().async {
            print("請(qǐng)求網(wǎng)絡(luò)")
            single(.success(text))
        }
        return Disposables.create()
    })
}

網(wǎng)絡(luò)請(qǐng)求為耗時(shí)操作愤惰,因此我們?cè)诋惒街衼?lái)完成苇经,直接發(fā)送序列,假裝我們請(qǐng)求了一次網(wǎng)絡(luò)宦言。

1扇单、普通方法實(shí)現(xiàn)textfield輸入序列的監(jiān)聽(tīng),并調(diào)取網(wǎng)絡(luò)請(qǐng)求方法:

let result = tf.rx.text.orEmpty.skip(1)
                .flatMap{
                    return self.network(text: $0)
                        .observeOn(MainScheduler.instance)
                        .catchErrorJustReturn("網(wǎng)絡(luò)請(qǐng)求失敗")
            }.share(replay: 1, scope: .whileConnected)
//網(wǎng)絡(luò)請(qǐng)求將發(fā)送多次請(qǐng)求
result.subscribe(onNext: { (val) in
    print("訂閱一:\(val) 線程:\(Thread.current)")
}).disposed(by: disposeBag)

result.subscribe(onNext: { (val) in
    print("訂閱二:\(val) 線程:\(Thread.current)")
}).disposed(by: disposeBag)

result.map{"\(($0 as! String).count)"}.bind(to: label1.rx.text).disposed(by: disposeBag)
result.map{"\($0)"}.bind(to: label2.rx.text).disposed(by: disposeBag)
  • flatMap將原序列轉(zhuǎn)換為Observables奠旺,將這些Observables的元素合并之后發(fā)出
  • observeOn選擇在哪個(gè)線程執(zhí)行
  • catchErrorJustReturn錯(cuò)誤處理蜘澜,將onError事件轉(zhuǎn)為onNext事件
  • share為多個(gè)觀察者共享資源,網(wǎng)絡(luò)請(qǐng)求只發(fā)送呢一次响疚,否則多個(gè)訂閱將會(huì)觸發(fā)多個(gè)請(qǐng)求

2鄙信、Driver實(shí)現(xiàn):

let result = tf.rx.text.orEmpty
    .asDriver()
    .flatMap {
        return self.network(text: $0).asDriver(onErrorJustReturn: "網(wǎng)絡(luò)請(qǐng)求失敗")
    }
result.map{"長(zhǎng)度:\(($0 as! String).count)"}
        .drive(label1.rx.text).disposed(by: disposeBag)
result.map{"\($0)"}.drive(label2.rx.text).disposed(by: disposeBag)
  • asDriver()將序列轉(zhuǎn)換為driver序列
  • map重新組合并生成新的序列
  • driver將元素在主線程中綁定到label1label2

相比非driver下的代碼實(shí)現(xiàn),driver序列省去了線程的設(shè)置忿晕,share數(shù)據(jù)共享設(shè)置装诡。

Driver源碼探索

斷點(diǎn)查看asDriver()方法:

extension ControlProperty {
    /// Converts `ControlProperty` to `Driver` trait.
    ///
    /// `ControlProperty` already can't fail, so no special case needs to be handled.
    public func asDriver() -> Driver<E> {
        return self.asDriver { _ -> Driver<E> in
            #if DEBUG
                rxFatalError("Somehow driver received error from a source that shouldn't fail.")
            #else
                return Driver.empty()
            #endif
        }
    }
}

ControlProperty的擴(kuò)展方法,返回了一個(gè)Driver<E>類践盼,DriverSharedSequence的別名鸦采,用來(lái)描述不同類型的序列,最后又調(diào)用了asDriver方法咕幻,而該方法在ObservableConvertibleType的擴(kuò)展中渔伯,一直追蹤會(huì)發(fā)現(xiàn)很多類都是繼承自ObservableConvertibleType下。

extension ObservableConvertibleType {
    public func asDriver(onErrorRecover: @escaping (_ error: Swift.Error) -> Driver<E>) -> Driver<E> {
        let source = self
            .asObservable()
            .observeOn(DriverSharingStrategy.scheduler)
            .catchError { error in
                onErrorRecover(error).asObservable()
            }
        return Driver(source)
    }
}

如上代碼也設(shè)置了observerOn方法肄程,來(lái)指定線程锣吼,繼續(xù)深入能夠發(fā)現(xiàn)DriverSharingStrategy.scheduler內(nèi)部指定的就是主線程,印證了上面所說(shuō)的Driver的執(zhí)行是在主線程的绷耍。最后初始化一個(gè)Driver對(duì)象返回吐限,看一下初始化過(guò)程,及對(duì)SharedSequence類的初始化褂始,代碼如下:

public struct SharedSequence<S: SharingStrategyProtocol, Element> : SharedSequenceConvertibleType {
    public typealias E = Element
    public typealias SharingStrategy = S

    let _source: Observable<E>

    init(_ source: Observable<E>) {
        self._source = S.share(source)
    }
}

此處調(diào)用了share并傳入了可觀察序列诸典,感覺(jué)好像在哪見(jiàn)過(guò),此處猜想它是用來(lái)共享序列的,使用lldb:po S.self查找share所在位置:RxCocoa.DriverSharingStrategy狐粱,cmd+點(diǎn)擊進(jìn)入舀寓,代碼如下:

public typealias Driver<E> = SharedSequence<DriverSharingStrategy, E>

public struct DriverSharingStrategy: SharingStrategyProtocol {
    public static var scheduler: SchedulerType { return SharingScheduler.make() }
    public static func share<E>(_ source: Observable<E>) -> Observable<E> {
        return source.share(replay: 1, scope: .whileConnected)
    }
}

在此處傳入的序列用來(lái)掉用share,使得當(dāng)前序列作為共享序列肌蜻,即driver序列為共享序列互墓。

六、Signal

Driver相似蒋搜,不會(huì)產(chǎn)生error事件篡撵,在主線程執(zhí)行,但不會(huì)像Driver一樣會(huì)給新觀察者發(fā)送上一次發(fā)送的元素豆挽。

使用如下:

let event : Driver<Void> = button.rx.tap.asDriver()
event.drive(onNext: {
    print("yahibo")
    event.drive(onNext: {
        print("yahibo1")
    }).disposed(by: self.disposeBag)
}).disposed(by: disposeBag)

運(yùn)行打印育谬,發(fā)現(xiàn)在點(diǎn)擊后重新訂閱的觀察者,會(huì)直接收到點(diǎn)擊事件帮哈,這是我們業(yè)務(wù)不允許的膛檀。下面再看Signal序列:

let event : Signal<Void> = button.rx.tap.asSignal()
event.emit(onNext: {
    print("yahibo")
    event.emit(onNext: {
        print("yahibo1")
    }).disposed(by: self.disposeBag)
}).disposed(by: disposeBag)

運(yùn)行結(jié)果,每一個(gè)新序列都會(huì)在點(diǎn)擊后觸發(fā)娘侍。

七咖刃、ControlEvent

專門(mén)用于描述UI控件所產(chǎn)生的事件,不會(huì)產(chǎn)生error事件憾筏,在主線程中監(jiān)聽(tīng)嚎杨。代碼如下:

1、監(jiān)聽(tīng)點(diǎn)擊事件

let event : ControlEvent<Void> = button.rx.tap.asControlEvent()
event.bind(onNext: {
    print("controllerEvent")
}).disposed(by: disposeBag)

2踩叭、監(jiān)聽(tīng)點(diǎn)擊事件并綁定數(shù)據(jù)到其他UI

let event : ControlEvent<Void> = button.rx.tap.asControlEvent()
event.map{"yahibo"}.bind(to: label1.rx.text).disposed(by: disposeBag)

總結(jié):

以上序列都是基于Observable的磕潮,是對(duì)其更高層的封裝,針對(duì)不同應(yīng)用場(chǎng)景設(shè)計(jì)容贝,簡(jiǎn)化不同場(chǎng)景下序列的使用流程自脯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市斤富,隨后出現(xiàn)的幾起案子膏潮,更是在濱河造成了極大的恐慌,老刑警劉巖满力,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焕参,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡油额,警方通過(guò)查閱死者的電腦和手機(jī)叠纷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)潦嘶,“玉大人涩嚣,你說(shuō)我怎么就攤上這事。” “怎么了航厚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵顷歌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我幔睬,道長(zhǎng)眯漩,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任麻顶,我火速辦了婚禮赦抖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘辅肾。我一直安慰自己摹芙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布宛瞄。 她就那樣靜靜地躺著,像睡著了一般交胚。 火紅的嫁衣襯著肌膚如雪份汗。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天蝴簇,我揣著相機(jī)與錄音杯活,去河邊找鬼。 笑死熬词,一個(gè)胖子當(dāng)著我的面吹牛旁钧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播互拾,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼歪今,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了颜矿?” 一聲冷哼從身側(cè)響起寄猩,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎骑疆,沒(méi)想到半個(gè)月后田篇,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡箍铭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年泊柬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诈火。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡兽赁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情闸氮,我是刑警寧澤剪况,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站蒲跨,受9級(jí)特大地震影響译断,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜或悲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一孙咪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧巡语,春花似錦翎蹈、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至枢赔,卻和暖如春澄阳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背踏拜。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工碎赢, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人速梗。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓肮塞,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親姻锁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子枕赵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345