YBImageBrowser 重構(gòu)心得:如何優(yōu)化架構(gòu)膝藕、性能、內(nèi)存咐扭?

前言

界面展示類型的輪子往往定制性需求比較多芭挽,常常讓人抓耳撓腮。這種接近業(yè)務(wù)的輪子如何設(shè)計才能兼顧便捷性和拓展性蝗肪?如何有效的優(yōu)化性能袜爪?如何控制內(nèi)存不至于 OOM ?本文以 YBImageBrowser 的重構(gòu)為切入點(diǎn)薛闪,盡量抽象提煉辛馆,談?wù)劰P者對以上問題的思考。

YBImageBrowser 是筆者 2018 年 4 月發(fā)布的開源項(xiàng)目豁延。時隔一年多昙篙,接近 1.3k stars,處理了 100+ issues诱咏,200+ commits苔可,30+ releases,兩次深度重構(gòu)袋狞。重構(gòu)的原因很簡單焚辅,無法忍受自己寫的拙劣代碼 ??映屋,另一方面這份代碼也承載了某種情懷。

下面就從幾個方面談?wù)勥@次重構(gòu)引出的值得分享的東西同蜻。

一棚点、圖片處理流程

一張圖片展示到屏幕上的流程:

對高清圖進(jìn)行壓縮和裁剪避免圖片超過最大支持紋理時 CPU 在主線程對圖片額外處理,同時也降低了圖片解碼后的內(nèi)存占用埃仪。

在這個流程中乙濒,有網(wǎng)絡(luò)下載、讀磁盤等 IO 密集型任務(wù)卵蛉,也有壓縮颁股、裁剪、解碼等 CPU 密集型任務(wù)傻丝,可見圖片的處理過程非常消耗硬件資源甘有,當(dāng)出現(xiàn)大量的圖片同時處理時將面臨一些挑戰(zhàn),如何減輕 CPU 的壓力葡缰、如何減輕內(nèi)存的負(fù)擔(dān)亏掀,時間和空間總是需要權(quán)衡取舍,有時它們互補(bǔ)泛释,有時它們互斥滤愕。

二、架構(gòu)設(shè)計

界面展示類型組件需要有良好的深度定制性怜校,這就對架構(gòu)設(shè)計要求較高间影,難點(diǎn)在于區(qū)分變量與不變量,各模塊職責(zé)劃分茄茁,以及合理的抽象魂贬。總的來說思維方向是不變的裙顽,落地到代碼需要做很多的變化和取舍付燥。

1、IOP 思想

IOP 是一個大家都知道的理念愈犹,但是落地到一個 UI 類型的組件中該如何實(shí)施键科?

保證深度定制性

YBImageBrowser 中默認(rèn)有圖片展示、視頻播放兩個模塊甘萧,當(dāng)用戶不滿足于此萝嘁,比如可能需要加入一個廣告模塊(類似于微博圖片瀏覽器最后一頁)。為了拓展起來無障礙扬卷,配置數(shù)據(jù)源時就不能用具體的類型牙言,抽象出一個協(xié)議:

/// 數(shù)據(jù)源數(shù)組
@property (nonatomic, copy) NSArray<id<YBIBDataProtocol>> *dataSourceArray;

如此用戶便能自由的拓展其它模塊,只是協(xié)議方法必須要明確職責(zé)怪得,不能包含具體模塊的業(yè)務(wù)咱枉。比如默認(rèn)實(shí)現(xiàn)的YBIBImageData<YBIBDataProtocol>類卑硫,圖片解碼壓縮等屬于這個模塊獨(dú)有的功能,所以不能讓<YBIBDataProtocol>協(xié)議有所感知蚕断。

當(dāng)然欢伏,使用繼承方式利用多態(tài)特性也能具有拓展能力,這樣的好處是能提供默認(rèn)實(shí)現(xiàn)亿乳,缺點(diǎn)是不夠靈活硝拧,侵入性過強(qiáng)。單從制定規(guī)則的角度說葛假,使用協(xié)議遠(yuǎn)比基類來得好障陶。

兼顧便捷性

圖片瀏覽器上面通常有一些工具欄,比如頁碼指示器聊训、長按彈出的表單等抱究,這些東西首先肯定要保證定制性,所以抽象一個協(xié)議是理所當(dāng)然的:

@property (nonatomic, copy) NSArray<id<YBIBToolViewHandler>> *toolViewHandlers;

使用數(shù)組是因?yàn)榭赡苡卸鄠€協(xié)議實(shí)現(xiàn)者带斑。既然這是約束工具視圖的協(xié)議鼓寺,為什么要用id類型而不是UIView?因?yàn)榭紤]到可能用戶需要用一個中介者來實(shí)現(xiàn)這個協(xié)議勋磕,然后由這個中介者管理所有的UIView妈候。

部分用戶是不希望由自己來實(shí)現(xiàn)這種功能的,所以有必要提供一個默認(rèn)實(shí)現(xiàn)類挂滓,默認(rèn)實(shí)現(xiàn)類中可能有與協(xié)議無關(guān)的屬性配置州丹,所以將其暴露出來讓用戶可以便捷的配置(這里是與協(xié)議同名的實(shí)現(xiàn)類):

// 可以修改其屬性
@property (nonatomic, weak, readonly) YBIBToolViewHandler *defaultToolViewHandler;

協(xié)議如何設(shè)計

協(xié)議可以讓組件主體向其它子模塊發(fā)送消息,但是當(dāng)子模塊想通過協(xié)議獲取組件主體的信息如何做杂彭?

直接通過協(xié)議方法:

@protocol
- (void)setName:(NSString *)name;
@end

這種方式需要子模塊將信息持有起來,更簡潔的方式是直接定義一個屬性:

@protocol
@property NSString *name;
@end

上面這兩種方式在name這個屬性是常量時比較簡單吓揪,若name會動態(tài)變化呢亲怠?那必然還需要寫更新邏輯。獲取動態(tài)信息更優(yōu)雅的方式是使用閉包:

@protocol
@property NSString *(^name)(void);
@end

主體中實(shí)現(xiàn)anyObject.name = ^NSString*{ return 動態(tài)計算結(jié)果 }柠辞,在子模塊中团秽,只需要調(diào)用self.name()就能獲取到實(shí)時的數(shù)據(jù)了。

有些方法可能會出現(xiàn)在多個協(xié)議中叭首,那抽象一些基協(xié)議就很有必要了习勤,這樣的好處不止是少寫幾句方法,在調(diào)用協(xié)議方法時還能復(fù)用代碼焙格,比如:

- (void)implementProtocolA:(id<ProtocolA>)anyObject { 
    協(xié)議有關(guān)图毕、具體類型無關(guān)的方法調(diào)用 
}

2、解耦的意義

需要理解解耦的目的眷唉,并不是一說架構(gòu)就是解耦予颤。

解開耦合的兩個模塊互相了解很少囤官,就像男女朋友,“分手”比較容易蛤虐,不拖泥帶水党饮;而互相關(guān)聯(lián)的兩個模塊就像夫妻,多數(shù)情況下他們一生不會分離驳庭,要分離就叫“離婚”刑顺,關(guān)乎兩邊家庭,還要走法律程序饲常,非常復(fù)雜蹲堂。

所以,模塊與模塊之間是否需要解耦不皆,判斷它們是要做夫妻還是做男女朋友贯城。

比如 YBImageBrowser 中的旋轉(zhuǎn)處理類、數(shù)據(jù)中介者霹娄,它們就不需要解耦能犯,抽離出來的目的只是為了方便管理和瘦身。

3犬耻、離散配置與集約配置

對于不同的實(shí)例對象踩晶,它們的功能最好能離散配置,集約配置僅作為便捷管理枕磁,這樣才能保證場景覆蓋完全渡蜻。

比如使用 YBImageBrowser 時,用戶需要對特定某張圖片進(jìn)行單獨(dú)的預(yù)處理计济,就需要能離散配置茸苇。

4、談?wù)?SDWebImage 和 YYImage

SDWebImage 5.0 優(yōu)化了很多東西沦寂,很重要的一點(diǎn)是將很多集約配置的功能改為了離散配置学密。以前只能在進(jìn)入 YBImageBrowser 時緩存配置,然后更改為組件需要的配置传藏,退出 YBImageBrowser 時將緩存的配置還原腻暮,非常蛋疼。

還更新了一個重要的類 SDAnimatedImage毯侦,大概看了一下源碼哭靖,兩個缺點(diǎn):不能支持普通圖片,意味著要明確某張圖片是動圖侈离,才能用這個類(組件中為了讓用戶對圖片類型無感知试幽,筆者就需要拓展普通圖片的處理,成本較高)霍狰;解碼之前也沒有暴露一個根據(jù)圖片大小決議是否解碼的接口抡草,所以在處理超清大圖不夠靈活饰及。而對于 YYImage,只有一個問題康震,就是沒有暴露是否解碼的接口燎含。

所以最終還是選擇了 YYImage,更改了源碼讓其可以通過圖片的大小來動態(tài)判斷是否解碼腿短。直接使用 SDWebImage 的下載模塊和緩存模塊屏箍,避免其框架內(nèi)部的額外圖片處理浪費(fèi)資源。

5橘忱、構(gòu)建子依賴

為了讓用戶可選集成赴魁,特意做了子依賴,默認(rèn)是沒有視頻播放模塊的钝诚,方便了對代碼量要求嚴(yán)格的團(tuán)隊(duì)颖御。

三、性能優(yōu)化

UI 類型組件的性能優(yōu)化凝颇,涉及算法復(fù)雜度的一般較少潘拱,多數(shù)情況都是利用硬件的能力進(jìn)行立竿見影的優(yōu)化。

1拧略、任務(wù)異步化

最容易想到的一步就是把處理圖片的任務(wù)盡量放子線程芦岂,這會讓主線程倍感輕松。而有時由于各種原因垫蛆,外部需要先進(jìn)行一些阻塞主線程的操作(比如訪問磁盤 IO)禽最,然后才將結(jié)果寫入。這時可以提供一個閉包袱饭,類似于:

data = ^UIImage *{
    return [UIImage customGetImageMethode];
}

如此川无,閉包代碼執(zhí)行線程就由組件控制了,同時還避免了持有讀取的這塊內(nèi)存虑乖,不過前提是這個閉包包含的是方法而不是結(jié)果舀透。

任務(wù)放異步線程時,線程上下文切換等會消耗一些時間决左,所以一般會降低任務(wù)的執(zhí)行速度,得益于多核設(shè)備走贪,讓我們可以在保證任務(wù)不阻塞主線程的前提下提升執(zhí)行效率佛猛,不過這需要在多個任務(wù)或者任務(wù)支持拆分時才能變得更快。

YBImageBrowser 幾乎將所有的耗時任務(wù)異步化了坠狡,對共有變量的修改都保證在同一線程所以避免了使用鎖继找。

2、分步緩存

YBImageBrowser 使用類實(shí)例來配置數(shù)據(jù)逃沿,數(shù)據(jù)處理后交付給 UICollectionViewCell 顯示婴渡。這也是很多 UI 類型組件的做法幻锁,將數(shù)據(jù)和界面分離,大概如下:

在數(shù)據(jù)的處理過程中边臼,需要將一些狀態(tài)交付給 Cell 做界面提示(比如 Loading哄尔、Toast),很多時候處理完成一個數(shù)據(jù)并非只有一個任務(wù)柠并。

對于 YBImageBrowser 來說岭接,每一張圖片都有一個這樣的數(shù)據(jù)模型,所以可能某些數(shù)據(jù)模型的任務(wù)會被中斷(這是后面要講的優(yōu)化)臼予,被中斷任務(wù)的數(shù)據(jù)模型有兩個結(jié)果鸣戴,一個是釋放,一個是待命粘拾。

當(dāng)這個數(shù)據(jù)模型是待命狀態(tài)窄锅,未來某一時刻恢復(fù)使用時,如果它之前做的“努力”白費(fèi)了缰雇,就需要返工做之前做過的任務(wù)入偷。所以為了減少重復(fù)“勞動”,可以對任務(wù)處理流程中產(chǎn)生的中間數(shù)據(jù)進(jìn)行緩存寓涨,恢復(fù)使用時直接從上次中斷的節(jié)點(diǎn)開始任務(wù)盯串,以此來優(yōu)化性能,典型的空間換時間戒良。

同時体捏,需要定義一些變量或枚舉,標(biāo)識當(dāng)前的狀態(tài)糯崎,在進(jìn)入不可重入異步任務(wù)之前做個判斷几缭,避免發(fā)起多個相同的異步任務(wù)。

3沃呢、數(shù)據(jù)預(yù)加載

預(yù)加載是一個常規(guī)的優(yōu)化思路年栓,UI 類型組件的數(shù)據(jù)預(yù)加載往往可以放在動畫轉(zhuǎn)場、數(shù)據(jù)內(nèi)容將要顯示時薄霜。

YBImageBrowser 會在開始轉(zhuǎn)場動畫時立即加載目標(biāo)數(shù)據(jù)模型某抓,一般零點(diǎn)幾秒的動效就能讓用戶無感知預(yù)加載了;在加載某一個數(shù)據(jù)模型時惰瓜,還會“均分”加載兩邊的數(shù)據(jù)模型否副,當(dāng)用戶滑動不是很快時,多數(shù)情況下一張圖片已經(jīng)加載好了崎坊。

預(yù)加載的邏輯需要根據(jù)具體的業(yè)務(wù)需求來處理备禀,圖片瀏覽器的滑動不會跳躍,所以預(yù)加載臨近的數(shù)據(jù)模型是個不錯的選擇。

4曲尸、任務(wù)中斷

前面也說了處理圖片的任務(wù)對硬件的消耗很大赋续,在使用圖片瀏覽器時,用戶快速的滑動圖片另患,將會讓大量的數(shù)據(jù)模型發(fā)起處理流程纽乱,這對 CPU 會造成巨大的壓力,也會讓內(nèi)存峰值飆升柴淘。

所以我們需要及時的中斷不重要的任務(wù)迫淹,騰出 CPU 資源和內(nèi)存做優(yōu)先級高的事,那么怎么判斷優(yōu)先級高低为严?

判斷優(yōu)先級高低需要根據(jù)具體的業(yè)務(wù)敛熬,在圖片瀏覽器中,數(shù)據(jù)模型有一個代理delegate用來接收處理結(jié)果第股,這個代理就是 Cell应民。筆者假定:當(dāng)delegate對象從有到無時,說明這個數(shù)據(jù)模型的任務(wù)可以中斷了夕吻。

delegate從有到無體現(xiàn)到界面上就是诲锹,用戶滑到了某張圖片,然后又劃開了這張圖片涉馅,那么就可以認(rèn)為归园,用戶短時間內(nèi)再滑回這種圖片的幾率較小。當(dāng)然這種假定是不嚴(yán)謹(jǐn)?shù)闹煽螅彩菣?quán)宜之計庸诱。

可重入方法的處理

對于同一個數(shù)據(jù)模型來說,它的異步任務(wù)可能是允許重入的晤揣,比如圖片瀏覽器的裁剪功能桥爽,有可能上一個裁剪任務(wù)未完成,下一個任務(wù)就發(fā)起了昧识,而上一個任務(wù)結(jié)果已經(jīng)沒有意義了钠四,那及時的中斷上一個任務(wù)就非常有必要了。

這種處理方案就比較傳統(tǒng)了跪楞,使用一個計數(shù)器遞增:

int32_t value = [_cuttingSentinel increase];
BOOL (^isCancelled)(void) = ^BOOL(void) {
       return value != self->_cuttingSentinel.value;
};

然后在異步任務(wù)的過程中缀去,添加足夠多的if(isCancelled()) return,盡量降低中斷的粒度就行了甸祭。

四朵耕、內(nèi)存優(yōu)化

圖片處理不光是一個 CPU 敏感的業(yè)務(wù),還是一個內(nèi)存敏感的業(yè)務(wù)淋叶,所以在上面做了提升性能的各種方案過后,還需要對內(nèi)存進(jìn)行控制,不然很容易就內(nèi)存警告或 OOM 了煞檩。

1处嫌、數(shù)據(jù)模型無關(guān)的緩存設(shè)計

在需要讓用戶配置不定數(shù)量的數(shù)據(jù)模型的組件設(shè)計中,一般使用數(shù)組或代理方法的方式配置斟湃,數(shù)組的特點(diǎn)就是始終會持有所有數(shù)據(jù)模型熏迹,代理的特點(diǎn)就是用完數(shù)據(jù)模型即扔掉。

那么凝赛,若數(shù)據(jù)模型緩存的數(shù)據(jù)將會占有大量的內(nèi)存怎么辦注暗?我們需要一套內(nèi)存管理及淘汰策略,那么如何來設(shè)計呢墓猎?

最容易想到的方案

既然數(shù)據(jù)模型緩存有大內(nèi)存捆昏,直接將數(shù)據(jù)模型釋放不就行了,那么就必須要讓用戶使用代理的方式配置數(shù)據(jù)源毙沾,才能用完就釋放骗卜。但是這樣又帶來一個問題,如果用一個釋放一個左胞,那么用戶切換到上一個數(shù)據(jù)又得重新加載了寇仓。

所以,還需要做一個局部的緩存烤宙,將一定數(shù)量的數(shù)據(jù)模型緩存起來(比如緩存 9 個)遍烦,最大限度保證用戶體驗(yàn)。

但是躺枕,這種方案無法支持?jǐn)?shù)組服猪。

優(yōu)化過后的方案

筆者建議的方式是,使用與數(shù)據(jù)模型無關(guān)的內(nèi)存管理策略屯远,具體做法如下:
1蔓姚、定義一個內(nèi)存管理中介者。
2慨丐、定義所有數(shù)據(jù)模型可訪問的內(nèi)存管理散列容器(key:數(shù)據(jù)模型地址, value:大內(nèi)存數(shù)據(jù))坡脐。
3、定義散列容器的數(shù)據(jù)淘汰策略(比如直接用 NSCache 控制數(shù)量)
4房揭、將數(shù)據(jù)模型產(chǎn)生的大內(nèi)存數(shù)據(jù)存入散列容器备闲,而自身不去引用。
5捅暴、使用時從散列容器中拿數(shù)據(jù)恬砂。
6、數(shù)據(jù)模型釋放時從散列容器中刪除數(shù)據(jù)蓬痒。

如此泻骤,便可以同時支持?jǐn)?shù)組和代理配置方式,進(jìn)行無障礙緩存淘汰了。

中介者是否使用單例

這個圖片內(nèi)存管理中介者第一反應(yīng)可能是做一個單例狱掂,實(shí)際上不需要演痒,如果是單例的話可能由于數(shù)據(jù)模型的內(nèi)存泄漏而導(dǎo)致單例內(nèi)存清除不徹底,做為一個常駐內(nèi)存是非城鞑遥可怕的鸟顺。

所以中介者應(yīng)該每一個圖片瀏覽器單獨(dú)一個,然后跟隨圖片瀏覽器的釋放而釋放器虾,把主要責(zé)任給一個人而不是分?jǐn)偟剿腥艘彩且粋€理念吧讯嫂,況且在這個場景下,跟隨圖片瀏覽器釋放是一個保底方式(數(shù)據(jù)模型釋放異常)兆沙。

正在使用數(shù)據(jù)的防護(hù)

這種方案帶來的問題就是當(dāng)前數(shù)據(jù)正在使用欧芽,然后中介者就將它釋放了,這會帶來異常挤悉,所以筆者額外使用一個散列容器來持有目前不能釋放的數(shù)據(jù)渐裸,當(dāng)數(shù)據(jù)模型的失去delegate時,移除這個散列容器對應(yīng)的數(shù)據(jù)装悲。

2昏鹃、圖片裁剪的優(yōu)化

當(dāng)圖片過大需要壓縮顯示,放大時圖片的”可視區(qū)域”表明了在原始圖片中的大小和位置诀诊,裁剪原圖的這個區(qū)域以顯示清晰圖片洞渤。當(dāng)原圖很大時這個區(qū)域也會比較大,如果直接讓原圖繪制在這樣一個上下文中會消耗很大的內(nèi)存属瓣,我們需要的僅僅是屏幕所顯示的大小就行了载迄,所以要將這個“可視區(qū)域”的較大圖繪制到更小的上下文中。

目前的解決方案是:先CGImageCreateWithImageInRect(...)生成CGImageRef(未解碼時不會造成內(nèi)存負(fù)擔(dān))抡蛙,然后將這個比較大的”可視區(qū)域”縮小到屏幕的大小范圍护昧,最后將CGImageRef繪制上去。

然而當(dāng)觸發(fā)裁剪的比例比較小時(比如放大 0.1 倍就觸發(fā)裁剪)粗截,仍然會消耗很大的 CPU 資源惋耙,為了減輕這種問題,組件內(nèi)部讓觸發(fā)裁剪的縮放比例與原圖的比例正相關(guān)動態(tài)變化熊昌。

3绽榛、低內(nèi)存設(shè)備降低性能

這是最后的補(bǔ)救措施,低內(nèi)存設(shè)備確實(shí)在加載數(shù)張比較清晰的圖片時會非常吃力婿屹,CPU 也不給力灭美,所以組件內(nèi)部在物理內(nèi)存較小的設(shè)備中直接降低性能,減少內(nèi)存占用昂利。

后語

每過段時間都會審視自己的代碼届腐,重構(gòu)是個苦力活铁坎,特別是代碼量比較大,邏輯較為復(fù)雜的項(xiàng)目犁苏,別看這篇文章三言兩語厢呵,因?yàn)槎际切┙Y(jié)論。

做完過后確實(shí)是有所收益的傀顾,但是收益不大,耗費(fèi)了大量的腦細(xì)胞碌奉,很多時間可能都是糾結(jié)于系統(tǒng)框架的各種坑短曾。對于 YBImageBrowser 理論上我是不會再大規(guī)模重構(gòu)了,花費(fèi)了太多時間赐劣。

天天看到各位大佬們刷題嫉拐,心里比較慌,所以還是要選擇一個收益比較大的方式學(xué)習(xí)魁兼,接下來開始加入刷題大軍婉徘?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市咐汞,隨后出現(xiàn)的幾起案子盖呼,更是在濱河造成了極大的恐慌,老刑警劉巖化撕,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件几晤,死亡現(xiàn)場離奇詭異,居然都是意外死亡植阴,警方通過查閱死者的電腦和手機(jī)蟹瘾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來掠手,“玉大人憾朴,你說我怎么就攤上這事∨绺耄” “怎么了众雷?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長魁衙。 經(jīng)常有香客問我报腔,道長,這世上最難降的妖魔是什么剖淀? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任纯蛾,我火速辦了婚禮,結(jié)果婚禮上纵隔,老公的妹妹穿的比我還像新娘翻诉。我一直安慰自己炮姨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布碰煌。 她就那樣靜靜地躺著舒岸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪芦圾。 梳的紋絲不亂的頭發(fā)上蛾派,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音个少,去河邊找鬼洪乍。 笑死,一個胖子當(dāng)著我的面吹牛夜焦,可吹牛的內(nèi)容都是我干的壳澳。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼茫经,長吁一口氣:“原來是場噩夢啊……” “哼巷波!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起卸伞,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤抹镊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后瞪慧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體髓考,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年弃酌,在試婚紗的時候發(fā)現(xiàn)自己被綠了氨菇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡妓湘,死狀恐怖查蓉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情榜贴,我是刑警寧澤豌研,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站唬党,受9級特大地震影響鹃共,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驶拱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一霜浴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蓝纲,春花似錦阴孟、人聲如沸晌纫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锹漱。三九已至,卻和暖如春慕嚷,著一層夾襖步出監(jiān)牢的瞬間哥牍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工喝检, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留砂心,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓蛇耀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親坎弯。 傳聞我的和親對象是個殘疾皇子纺涤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345