Swift 復(fù)雜視圖多事件回調(diào)處理方案思考

開(kāi)發(fā)中難免有需要制作復(fù)雜 UI 的 ViewController,子視圖一層套一層窃肠,夸張時(shí)層層視圖都有事件回調(diào),而我們只想在 “C” 中接收回調(diào)并處理,能讓代碼明明白白在業(yè)務(wù)處理層中展現(xiàn)削樊,讓場(chǎng)景易于維護(hù)、低耦合兔毒、迭代時(shí)清晰高效是我們所要達(dá)成的最終目的漫贞。

這篇文章主要是對(duì)結(jié)合《一種基于ResponderChain的對(duì)象交互方式》的方式與《關(guān)于使用 Swift 協(xié)議確保類(lèi)型的思考》的思考與實(shí)踐。文中涉及到有關(guān) Swift 消息派發(fā)機(jī)制知識(shí)育叁,推薦文章《深入理解 Swift 派發(fā)機(jī)制》

貫穿全文的例子

假設(shè)一個(gè)業(yè)務(wù)場(chǎng)景作為示例:做一個(gè)關(guān)于天空復(fù)雜視圖(View)迅脐,天空中有一個(gè)太陽(yáng)(View),太陽(yáng)里有太陽(yáng)黑子(View)豪嗽,這里點(diǎn)擊天空或太陽(yáng)谴蔑、太陽(yáng)的Subview都要直接在(ViewController)中做出相應(yīng)處理。

SkyResponderChainPlayground.swift.png

為了讓代碼簡(jiǎn)潔更好說(shuō)明龟梦,點(diǎn)擊事件使用簡(jiǎn)單的 touchesBegan(_:with:) 方法隐锭。

代理或閉包回調(diào)

閉包相比代理能少很多 XXXViewDelegate 及其實(shí)現(xiàn),但是從架構(gòu)上看计贰,closure 的處理代碼清晰程度必然比不上 delegate钦睡,因?yàn)閯?chuàng)建時(shí)機(jī)等關(guān)系,closure 的代碼可能散落各地躁倒,而 delegate 回調(diào)可以放在一目了然的位置荞怒,更加易于debug。

Delegates Closure.png

Delegates Closure 交互模式在層級(jí)單一樱溉,事件較少的業(yè)務(wù)邏輯里使用完全沒(méi)毛病挣输。然而在這個(gè)場(chǎng)景中,視圖越來(lái)越復(fù)雜后福贞,如例子中處理太陽(yáng)黑子的事件撩嚼,甚至點(diǎn)擊太陽(yáng)黑子時(shí)也要觸發(fā)點(diǎn)擊太陽(yáng)的事件就非常難受,我僅僅是想知道我點(diǎn)了一下太陽(yáng)黑子而已。層級(jí)增加隨之而來(lái)的問(wèn)題是代碼層級(jí)過(guò)多完丽,顯得臃腫維護(hù)吃力恋技。

// SunView.swift
protocol SunViewDelegate: class {
    /// 從太陽(yáng)黑子 View 傳過(guò)來(lái)的,繼續(xù)向 上層傳遞
    func sunView(_ view: SunView, didTapSunspot sunspotId: Int)
    /// 點(diǎn)擊太陽(yáng) View
    func sunViewDidTap(_ view: SunView)
}
class SunView: UIView {
    weak var delegate: SunViewDelegate?
    ...
}
// SunspotView.swift
protocol SunspotViewDelegate: class {
    /// 太陽(yáng)黑子點(diǎn)擊
    func sunspotDidTap(_ view: SunspotView)
}
class SunspotView: UIView {
    weak var delegate: SunspotViewDelegate?
    ...
}

用繼承抽象 Delegate

import UIKit

protocol SkyActionType {}

protocol SkyViewDelegate: class {
    func homeSubview(_ view: UIView, didTap action: HomeActionType)
}

class SkyBaseView: UIView {
    weak var delegate: HomeViewDelegate?
    func updateView(model: Model) {
        fatalError("[SkyBaseView] Unrealized")
    }
}

// 使用
class SunView: SkyBaseView { override func updateView(model: Model) { ... } }
class SunspotView: SkyBaseView { override func updateView(model: Model) { ... } }
...

這是筆者以前使用過(guò)的一套模式逻族,這樣繼承存在很大問(wèn)題

  • 1)所有子視圖都存在 Action
  • 2)所有視圖都需要實(shí)現(xiàn) updateView
  • 3)對(duì) Model 要求高

在功能迭代時(shí)蜻底,很難滿足上述幾點(diǎn),即便 updateView 默認(rèn)實(shí)現(xiàn)處不給激進(jìn)的 fatalError 錯(cuò)誤處理聘鳞,不能保證將來(lái)版本的迭代后的 View updateView 方法傳值還是 Model薄辅,要做額外泛型等處理,或是有的視圖根本不存在回調(diào)抠璃。

把 SkyViewController 的 Subviews 繼承一個(gè)基類(lèi)好處是可以減少代碼行數(shù)站楚,將事件封裝到一個(gè)代理中,便于修改與刪除原有邏輯搏嗡。但由于層級(jí)多窿春、隱藏關(guān)鍵代碼的緣故導(dǎo)致代碼可讀性差,過(guò)度依賴也讓代碼難以重用可維護(hù)性較差采盒,局限性大旧乞,即便完全理清代碼邏輯后,新增需求也因?yàn)闆](méi)有代碼提示的緣故容易漏寫(xiě)事件處理磅氨。復(fù)雜層級(jí)的視圖這么做也沒(méi)有解決層層回調(diào)代碼過(guò)與復(fù)雜的問(wèn)題尺栖。

我相信大家都碼出過(guò)這樣的代碼,雖然本意是想讓代碼更加優(yōu)美悍赢,但顯然還有更好的別的實(shí)現(xiàn)方式决瞳。

響應(yīng)鏈

如何汲取繼承的好處,將事件放在一塊兒維護(hù)左权?

ResponderChain 就是一個(gè)可以實(shí)現(xiàn)這樣需求的機(jī)制皮胡。雖說(shuō) UIResponder 也是逐級(jí)傳遞,但對(duì)開(kāi)發(fā)者來(lái)說(shuō)不必逐級(jí)實(shí)現(xiàn)傳遞功能赏迟。只需要 extension UIResponder 后添加一個(gè)傳遞方法屡贺。其原理不是文章主要內(nèi)容,不過(guò)多贅述锌杀,資料非常多甩栈。

Responder Chain.png

圖中 SunspotView 發(fā)送事件(黑色箭頭),在響應(yīng)鏈后端 SunView 與 SkyView 都可以按需獲取 SunspotView 事件(灰色箭頭)來(lái)實(shí)現(xiàn)所需功能糕再。如果在 SunView 中不需要對(duì) SunspotView 的點(diǎn)擊事件做處理量没,開(kāi)發(fā)者則不需要考慮事件在 SunView 的傳遞實(shí)現(xiàn)。

Swift 代碼實(shí)現(xiàn):

enum SkyBehavior: String {
    case clickSky = "clickSky"
    case clickSun = "clickSun"
    case clickSunspot = "clickSunspot"
}

extension UIResponder {
    @objc func routerEvent(name: String, userInfo: [AnyHashable: Any]?) {
        next?.routerEvent(name: name, userInfo: userInfo)
    }
}

UIResponder 傳遞事件實(shí)現(xiàn)突想。

// SkyViewController.swift
class SkyViewController: UIViewController {
    ...
    // 獲取所有子視圖事件
    override func routerEvent(name: String, userInfo: [AnyHashable : Any]?) {
        guard let event = SkyBehavior(rawValue: name) else { return }
        switch event {
        case .clickSky: print("[Sky] click sky")
        case .clickSun: print("[Sky] click sun")
        case .clickSunspot: print("[Sky] click sun spot")
        }
    }
}

在天空 VC SkyViewController 中處理事件并停止響應(yīng)鏈繼續(xù)傳遞殴蹄。
SkyViewController × UIWindow -> UIApplication -> XCPAppDelegate

由于 Swift 中類(lèi)的拓展函數(shù)是被靜態(tài)派發(fā)的究抓,所以無(wú)法被子類(lèi)繼承,我們也不能將代碼直接添加到 UIResponder 的聲明域中用函數(shù)表來(lái)派發(fā)函數(shù)袭灯。解決辦法只能是利用 NSObject 拓展消息派發(fā)函數(shù)刺下,在 extension 中加 @objc dynamic 前綴。

如果在點(diǎn)擊太陽(yáng)黑子 SunspotView 的同時(shí)也觸發(fā)點(diǎn)擊太陽(yáng)的事件稽荧,只需要在 SunView 中重寫(xiě)方法橘茉,為了保證不在這里斷鏈,必須補(bǔ)上super.routerEvent(name: name, userInfo: userInfo)姨丈。

class SunView: UIView {
    ...
    override func routerEvent(name: String, userInfo: [AnyHashable : Any]?) {
        super.routerEvent(name: name, userInfo: userInfo)
        if name == SkyBehavior.clickSunspot.rawValue {
            routerEvent(name: SkyBehavior.clickSun.rawValue, userInfo: nil)
        }
    }
}

代碼SkyExtensionResponderChainPlayground.swift 可以在這個(gè) gist 里找到畅卓。

ResponderChain 優(yōu)化

主要存在的問(wèn)題:

  • 上述基于 ResponderChain 的交互用例功能被分發(fā)到了全局,所有繼承 UIResponder 的控件全部可以 router 局部的 SkyBehavior构挤。實(shí)際業(yè)務(wù)中髓介,現(xiàn)在 ResponderChain 的實(shí)現(xiàn)顯然是不合理,當(dāng)務(wù)之急是把發(fā)送事件的方法范圍縮小筋现,縮小到只有這個(gè)SkyViewController和與之相關(guān)的視圖才擁有這個(gè)功能。
  • userInfo: [AnyHashable: Any]? 的類(lèi)型強(qiáng)制性不高箱歧,啥都可以往里面?zhèn)鞣桑?yàn)證功能時(shí)還得跑起來(lái)看回調(diào)是否到位。
使用泛型協(xié)議

喵神在文章開(kāi)頭提到里提到:「相比于 Objective-C 這類(lèi)“動(dòng)態(tài)”語(yǔ)言呀邢,Swift 在類(lèi)型安全上強(qiáng)制性要高出許多洒沦。配合上協(xié)議和 associatedtype,更是能做到另一個(gè)極致价淌,很多時(shí)候可以讓我們寫(xiě)出“無(wú)腦”的申眼,能通過(guò)編譯就不會(huì)有太大問(wèn)題的代碼〔跻拢」

Swift 是一門(mén)強(qiáng)類(lèi)型語(yǔ)言括尸,Swifter 更喜歡盡量把東西放在明面上,非工具類(lèi)業(yè)務(wù)少些動(dòng)態(tài)病毡,更愿意花時(shí)間做一勞永逸的事濒翻。Delegate 或 Closure 很大的優(yōu)點(diǎn)在于不論層級(jí)多么復(fù)雜,修改功能時(shí)會(huì)產(chǎn)生大量編譯錯(cuò)誤啦膜,等到開(kāi)發(fā)者處理完編譯錯(cuò)誤時(shí)有送,功能基本上也就修改完畢,而現(xiàn)在實(shí)現(xiàn)的 ResponderChain 并不擁有這個(gè)特性僧家,新增回調(diào)后忘了修改實(shí)現(xiàn)處代碼也能編譯通過(guò)雀摘,這只會(huì)增加 Debug 的時(shí)間。

實(shí)際業(yè)務(wù)中往往存在數(shù)據(jù)回調(diào)八拱,由于 router(event:) 函數(shù)是消息派發(fā)的阵赠,參數(shù)必須也是繼承 NSObject 的子類(lèi)涯塔,導(dǎo)致 SkyBehavior 枚舉不能用 Swift 的關(guān)聯(lián)值這個(gè)美妙的特性。利用泛型改造:

public protocol ResponderChainEventType {}

public protocol ResponderChainType {
    func router<Event>(event: Event) where Event : ResponderChainEventType
}

extension ResponderChainType where Self: UIResponder {
    public func router<Event>(event: Event) where Event : ResponderChainEventType {
        // Responder handler
        if let n = next as? SkyViewController {
            n.router(event: event)
        } else if let n = next as? SunView {
            n.router(event: event)
        }
        // Other hander ...
        else {
            next?.router(event: event)
        }
    }
}

extension UIResponder: ResponderChainType {}

改造后 ResponderChainType 對(duì) router 泛型包裝豌注,在拓展中這樣一來(lái)伤塌,之前的func routerEvent(name: String, userInfo: [AnyHashable : Any]?)直接改造成func router<Event>(event: Event) where Event : ResponderChainEventType,實(shí)現(xiàn)枚舉回調(diào)事件轧铁,其中 ResponderChainEventType 是事件的泛型約束每聪,約束泛型 Event ,操作更加安全可靠齿风。

enum SkyBehavior: ResponderChainEventType {
    case clickSky
    case clickSun
    case clickSunspot(id: Int)
}
// SkyViewController.swift
class SkyViewController: UIViewController {
    ...
    func router<Event>(event: Event) where Event : ResponderChainEventType {
        guard let e = event as? SkyBehavior else { return }
        switch e {
        case .clickSky: print("[Sky] click sky")
        case .clickSun: print("[Sky] click sun")
        case .clickSunspot(let sunspotId): print("[Sky] click sunspot - id: \(sunspotId)")
        }
    }
}

優(yōu)化后 userInfo 里的內(nèi)容被關(guān)聯(lián)到枚舉上药薯,Behavior 枚舉中也能方便添加回調(diào)傳值,代碼更加優(yōu)雅直觀救斑,作用域明確童本。代碼SkyProtocolResponderChainPlayground.swift 可以在這個(gè) gist 里找到。

視圖回調(diào)內(nèi)容強(qiáng)制性比之前更強(qiáng)脸候,比如在刪除太陽(yáng)黑子的 id 回調(diào)后穷娱,搞定各處的編譯錯(cuò)誤后,功能也已經(jīng)搞定了运沦。

美中不足

用于處理事件的 UIResponder(如 SkyViewController)必須在 extension ResponderChainTyperouter<Event>(:)的注釋// Responder handler// Other hander ...之間部分中聲明泵额。

extension ResponderChainType where Self: UIResponder {
    public func router<Event>(event: Event) where Event : ResponderChainEventType {
        // Responder handler
        if let n = next as? SkyViewController {
            n.router(event: event)
        }
        ...
    }
}

在當(dāng)前版本 Swift 4.0 的派發(fā)機(jī)制下,協(xié)議的 extension 都會(huì)使用直接派發(fā)携添,View 繼承于 UIResponder嫁盲,ResponderChainType 的拓展下,并不能確定 next 的具體類(lèi)型烈掠,只能多寫(xiě)幾行將傳遞的 UIResponder 定位到我們需要的那個(gè)后調(diào)用 router(event:) 方法羞秤。這個(gè)位置的判斷是必要的,但未實(shí)現(xiàn)也是不會(huì)報(bào)錯(cuò)的左敌,容易造成遺漏瘾蛋。

雖然這個(gè)實(shí)現(xiàn)并不是最完美,但相對(duì)直接利用 NSObject 消息派發(fā)的實(shí)現(xiàn)方式母谎,已經(jīng)大大降低了使用時(shí)的不確定性瘦黑。

等到 Swift 在今后更新迭代派發(fā)方式發(fā)生變化,亦或是筆者悟出更完美的解決辦法再來(lái)更新解決這個(gè)不足奇唤。也歡迎大神提點(diǎn)幸斥。

模擬業(yè)務(wù)增刪改

搞了這么多,無(wú)非是想在功能的增刪改時(shí)更加舒適咬扇,在業(yè)務(wù)邏輯變更時(shí)可以少燒些腦細(xì)胞甲葬。怎么驗(yàn)證基于 ResponderChain 的交互真的好用呢?

實(shí)現(xiàn)一個(gè)功能越繁瑣懈贺、代碼分布位置越廣時(shí)经窖,修改時(shí)就越容易遺漏坡垫,我們可以簡(jiǎn)單模擬一下并極度細(xì)化業(yè)務(wù)迭代可能發(fā)生的事,以此分別對(duì)比 Delegate 與 ResponderChain 增刪改所需步驟的繁瑣程度画侣。

SkyViewController.png
增刪:新增太陽(yáng)耀斑視圖 SolarFlareView冰悠,增加點(diǎn)擊事件

增和刪其實(shí)是一樣的,哪里加的代碼配乱,刪的時(shí)候就得回哪兒刪溉卓,所以這里代碼只舉例增的情況,把增與刪放在一塊分析搬泥。

// ResponderChain
// 1 - 在 Behavior 中添加點(diǎn)擊事件
enum SkyBehavior: ResponderChainEventType {
    ...
    case clickSolarFlare
}
// 2 - 創(chuàng)建視圖
// 3 - 添加點(diǎn)擊事件桑寨,發(fā)送事件
class SolarFlareView: UIView {
    ...
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        router(event: SkyBehavior.clickSolarFlare)
    }
}
// 4 - 處理事件 case
class SkyViewController: UIViewController {
    ...
    func router<Event>(event: Event) where Event : ResponderChainEventType {
        guard let e = event as? SkyBehavior else { return }
        switch e {
        ...
        case .clickSolarFlare: print("[Sky] click solar flare")
        }
    }
}
// Delegates
// 1 - 添加視圖 Delegate 聲明
protocol SolarFlareViewDelegate: class {
    func solarFlareViewDidTap(_ view: SolarFlareView)
}
// 2 - 創(chuàng)建視圖
// 3 - 視圖內(nèi)聲明 weak var delegate
// 4 - 添加點(diǎn)擊事件,發(fā)送事件
class SolarFlareView: UIView {
    weak var delegate: SolarFlareViewDelegate?
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        delegate?. solarFlareViewDidTap(self)
    }
}
// 5 - 回調(diào)點(diǎn)擊事件到 SunView 中
class SunView: UIView {
    let solarFlare = SolarFlareView(frame: .solarFlare)
    override init(frame: CGRect) {
        super.init(frame: frame)
        ...
        spotView.delegate = self
    }
    ...
}
// 6 - 回調(diào) 太陽(yáng)耀斑視圖
protocol SunViewDelegate: class {
    ...
    func sunView(_ view: SunView, didTapSolarFlare: SolarFlareView)
}
// 7 - 實(shí)現(xiàn) SolarFlareViewDelegate 繼續(xù)傳遞
extension SunView: SolarFlareViewDelegate {
    func solarFlareViewDidTap(_ view: SolarFlareView) {
        delegate?. sunView(self, didTapSolarFlare: view)
    }
}
// 8 - 處理事件
extension SkyViewController: SunViewDelegate {
    ...
    func sunView(_ view: SunView, didTapSolarFlare: SolarFlareView) {
        print("[Sky] click solar flare")
    }
}

基于 ResponderChain 交互用了 4 步忿檩,而用 Delegates 居然達(dá)到 8 步之多尉尾,其中 Delegates 最繁瑣的流程在太陽(yáng)視圖 SunView 中,相比前者不但步驟多燥透,代碼也分布在各個(gè)文件視圖中沙咏,刪除也需要翻遍各個(gè)文件。

2)改:修改太陽(yáng)耀斑視圖班套,在點(diǎn)擊SolarFlareView事件中回調(diào) id (注釋老的實(shí)現(xiàn)便于對(duì)比)
// ResponderChain
// 1 - 修改事件芭碍,關(guān)聯(lián)回調(diào)
enum SkyBehavior: ResponderChainEventType {
    ...
    // case clickSolarFlare
    case clickSolarFlare(id: Int)
}
// 2 - 修改回調(diào)傳遞
class SolarFlareView: UIView {
    ...
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // router(event: SkyBehavior.clickSolarFlare)
        router(event: SkyBehavior.clickSolarFlare(id: 1))
    }
}
// 3 - 修改處理事件 case 獲得 id
class SkyViewController: UIViewController {
    ...
    func router<Event>(event: Event) where Event : ResponderChainEventType {
        guard let e = event as? SkyBehavior else { return }
        switch e {
        ...
        // case .clickSolarFlare: print("[Sky] click solar flare")
        case .clickSolarFlare(let id): print("[Sky] click solar flare, id: \(id)")
        }
    }
}
// Delegates
// 1 - 修改代理聲明
protocol SolarFlareViewDelegate: class {
    // func solarFlareViewDidTap(_ view: SolarFlareView)
    func solarFlareView(_ view: SolarFlareView didTap id: Int)
}
// 2 - 修改回調(diào)傳遞
class SolarFlareView: UIView {
    weak var delegate: SolarFlareViewDelegate?
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // delegate?. solarFlareViewDidTap(self)
        delegate?. solarFlareView(self, didTap id: 1)
    }
}
// 3 - SunView 中修改代理
protocol SunViewDelegate: class {
    ...
    // func sunView(_ view: SunView, didTapSolarFlare: SolarFlareView)
    func sunView(_ view: SunView, didTapSolarFlare: SolarFlareView, solarFlareId: Int)
}
// 4 - SunView 中修改調(diào)用
extension SunView: SolarFlareViewDelegate {
    // func solarFlareViewDidTap(_ view: SolarFlareView) {
    func solarFlareView(_ view: SolarFlareView didTap id: Int)
        // delegate?. sunView(self, didTapSolarFlare: view)
        delegate?.sunView(self, didTapSolarFlare: view, solarFlareId: id)
    }
}
// 5 - 修改處理事件處
extension SkyViewController: SunViewDelegate {
    ...
    // func sunView(_ view: SunView, didTapSolarFlare: SolarFlareView) {
    //     print("[Sky] click solar flare")
    // }
    func sunView(_ view: SunView, didTapSolarFlare: SolarFlareView, solarFlareId: Int) {
        print("[Sky] click solar flare, id: \(solarFlareId)")
    }
}

基于 ResponderChain 交互用了 3 步,Delegates 用了 5 步孽尽,主要區(qū)別也是在于 SunView 中的修改,多了幾個(gè)傳遞步驟忧勿。

從業(yè)務(wù)的增加上對(duì)比來(lái)說(shuō)杉女,使用基于 ResponderChain 的交互后增刪改迭代功能的步驟都會(huì)變少,單獨(dú)添置內(nèi)容時(shí)也少有影響到別的模塊鸳吸,代碼耦合度降低熏挎,更為清爽。

總結(jié)

視圖多事件回調(diào)處理方案

  • Subviews 視圖層級(jí)只有一層晌砾、子視圖回調(diào)方法較少時(shí) 用DelegatesClosure
  • Subviews 視圖層級(jí)只有一層坎拐、子視圖較多(如3個(gè)以上認(rèn)為已經(jīng)非常繁瑣) 用ResponderChain
  • Subviews 視圖層級(jí)超過(guò)一層 用ResponderChain
  • 不要濫用繼承
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市养匈,隨后出現(xiàn)的幾起案子哼勇,更是在濱河造成了極大的恐慌,老刑警劉巖呕乎,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件积担,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡猬仁,警方通過(guò)查閱死者的電腦和手機(jī)帝璧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)先誉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人的烁,你說(shuō)我怎么就攤上這事褐耳。” “怎么了渴庆?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵铃芦,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我把曼,道長(zhǎng)杨帽,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任嗤军,我火速辦了婚禮注盈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘叙赚。我一直安慰自己老客,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布震叮。 她就那樣靜靜地躺著胧砰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪苇瓣。 梳的紋絲不亂的頭發(fā)上尉间,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音击罪,去河邊找鬼哲嘲。 笑死,一個(gè)胖子當(dāng)著我的面吹牛媳禁,可吹牛的內(nèi)容都是我干的眠副。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼竣稽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼囱怕!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起毫别,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤娃弓,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后拧烦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體忘闻,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年恋博,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了齐佳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片私恬。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖炼吴,靈堂內(nèi)的尸體忽然破棺而出本鸣,到底是詐尸還是另有隱情,我是刑警寧澤硅蹦,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布荣德,位于F島的核電站,受9級(jí)特大地震影響童芹,放射性物質(zhì)發(fā)生泄漏涮瞻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一假褪、第九天 我趴在偏房一處隱蔽的房頂上張望署咽。 院中可真熱鬧,春花似錦生音、人聲如沸宁否。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)慕匠。三九已至,卻和暖如春域醇,著一層夾襖步出監(jiān)牢的瞬間台谊,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工譬挚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留青伤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓殴瘦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親号杠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蚪腋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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

  • 1、通過(guò)CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫(kù)組件 SD...
    陽(yáng)明先生_x閱讀 15,968評(píng)論 3 119
  • 小鎮(zhèn)的春天 綠色環(huán)繞 河水作伴 喚人 誘人 迷人 更喜人 (因?yàn)樘鞖庑∮赀^(guò)后 天色較陰 部分后期snapseed調(diào)...
    柚子soul閱讀 234評(píng)論 0 0
  • 注:北海文中常見(jiàn)細(xì)膩的溫暖堂飞,卻不想昨夜在《生活中的四季歌》一文中卻暗藏內(nèi)心晦澀的悲傷灌旧,見(jiàn)我蜻蜓點(diǎn)水的評(píng)論以后绑咱,忍不...
    簡(jiǎn)村小吹閱讀 393評(píng)論 11 6
  • “繞口令”用日語(yǔ)說(shuō)是“早口言葉”衡蚂。日語(yǔ)中的繞口令也非常有意思窿克。尤其初學(xué)日語(yǔ),為了練舌頭和提升對(duì)假名的敏感度毛甲,我們會(huì)...
    喜姐閱讀 1,438評(píng)論 2 3