優(yōu)化UITableViewCell高度計(jì)算的那些事

原文地址:http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/?

摘要:在將UITableView+FDTemplateLayoutCell自動(dòng)算高工具更新至1.2版本之后普办,百度知道iOS團(tuán)隊(duì)對(duì)UITableViewCell利用AutoLayout自動(dòng)高度計(jì)算和UITableView滑動(dòng)優(yōu)化進(jìn)行了總結(jié),以及RunLoop實(shí)踐技巧。

前言

這篇文章是我和我們團(tuán)隊(duì)最近對(duì)UITableViewCell利用AutoLayout自動(dòng)高度計(jì)算和UITableView滑動(dòng)優(yōu)化的一個(gè)總結(jié)宝与。從這篇文章里裕膀,你可以讀到:

UITableView高度計(jì)算和估算的機(jī)制

不同iOS系統(tǒng)在高度計(jì)算上的差異

iOS8 self-sizingcell

UITableView+FDTemplateLayoutCell如何用一句話解決高度問題

UITableView+FDTemplateLayoutCell中對(duì)RunLoop的使用技巧

UITableViewCell高度計(jì)算

rowHeight

UITableView

是我們?cè)偈煜げ贿^的視圖了勿锅,它的delegate和datasource回調(diào)不知寫了多少次伍派,也不免遇到UITableViewCell高度計(jì)算的事灭美。UITableView詢問cell高度有兩種方式爽彤。一種是針對(duì)所有Cell具有固定高度的情況养盗,通過:

self.tableView.rowHeight = 88;

上面的代碼指定了一個(gè)所有cell都是88高度的UITableView,對(duì)于定高需求的表格适篙,強(qiáng)烈建議使用這種(而非下面的)方式保證不必要的高度計(jì)算和調(diào)用往核。rowHeight屬性的默認(rèn)值是44,所以一個(gè)空的UITableView顯示成那個(gè)樣子嚷节。

另一種方式就是實(shí)現(xiàn)UITableViewDelegate中的:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {? ? // return xxx}

需要注意的是聂儒,實(shí)現(xiàn)了這個(gè)方法后虎锚,rowHeight的設(shè)置將無效。所以衩婚,這個(gè)方法適用于具有多種cell高度的UITableView窜护。

estimatedRowHeight

這個(gè)屬性iOS7就出現(xiàn)了,文檔是這么描述它的作用的:

If the table contains variable height rows, it might be expensiveto calculate all their heights when the table loads. Usingestimation allows you to defer some of the cost of geometrycalculation from load time to scrolling time.

恩谅猾,聽上去蠻靠譜的柄慰。我們知道,UITableView是個(gè)UIScrollView税娜,就像平時(shí)使用UIScrollView一樣坐搔,加載時(shí)指定contentSize后它才能根據(jù)自己的bounds、contentInset敬矩、contentOffset等屬性共同決定是否可以滑動(dòng)以及滾動(dòng)條的長(zhǎng)度概行。而UITableView在一開始并不知道自己會(huì)被填充多少內(nèi)容,于是詢問datasource個(gè)數(shù)和創(chuàng)建cell弧岳,同時(shí)詢問delegate這些cell應(yīng)該顯示的高度凳忙,這就造成它在加載的時(shí)候浪費(fèi)了多余的計(jì)算在屏幕外邊的cell上。和上面的rowHeight很類似禽炬,設(shè)置這個(gè)估算高度有兩種方法:

self.tableView.estimatedRowHeight = 88;// or- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {? ? // return xxx}

有所不同的是涧卵,即使面對(duì)種類不同的cell,我們依然可以使用簡(jiǎn)單的estimatedRowHeight屬性賦值腹尖,只要整體估算值接近就可以柳恐,比如大概有一半cell高度是44,一半cell高度是88热幔, 那就可以估算一個(gè)66乐设,基本符合預(yù)期。

說完了估算高度的基本使用绎巨,可以開始吐槽了:

設(shè)置估算高度后近尚,contentSize.height根據(jù)“cell估算值 xcell個(gè)數(shù)”計(jì)算,這就導(dǎo)致滾動(dòng)條的大小處于不穩(wěn)定的狀態(tài)场勤,contentSize會(huì)隨著滾動(dòng)從估算高度慢慢替換成真實(shí)高度戈锻,肉眼可見滾動(dòng)條突然變化甚至“跳躍”。

若是有設(shè)計(jì)不好的下拉刷新或上拉加載控件和媳,或是KVO了contentSize或contentOffset屬性格遭,有可能使表格滑動(dòng)時(shí)跳動(dòng)。

估算高度設(shè)計(jì)初衷是好的窗价,讓加載速度更快如庭,那憑啥要去侵害滑動(dòng)的流暢性呢叹卷,用戶可能對(duì)進(jìn)入頁面時(shí)多零點(diǎn)幾秒加載時(shí)間感覺不大撼港,但是滑動(dòng)時(shí)實(shí)時(shí)計(jì)算高度帶來的卡頓是明顯能體驗(yàn)到的坪它,個(gè)人覺得還不如一開始都算好了呢(iOS8更過分,即使都算好了也會(huì)邊劃邊計(jì)算)帝牡。

iOS8 self-sizingcell

具有動(dòng)態(tài)高度內(nèi)容的cell一直是個(gè)頭疼的問題往毡,比如聊天氣泡的cell,frame布局時(shí)代通常是用數(shù)據(jù)內(nèi)容反算高度:

CGFloat height = textHeightWithFont() + imageHeight + topMargin + bottomMargin + ...;

供UITableViewDelegate調(diào)用時(shí)很可能是個(gè)cell的類方法:

@interfaceBubbleCell : UITableViewCell+ (CGFloat)heightWithEntity:(id)entity;@end

各種魔法margin加上耦合了屏幕寬度靶溜。

AutoLayout時(shí)代好了不少开瞭,提供了-systemLayoutSizeFittingSize:的API,在contentView中設(shè)置約束后罩息,就能計(jì)算出準(zhǔn)確的值嗤详;缺點(diǎn)是計(jì)算速度肯定沒有手算快,而且這是個(gè)實(shí)例方法瓷炮,需要維護(hù)專門為計(jì)算高度而生的template

layoutcell葱色,它還要求使用者對(duì)約束設(shè)置的比較熟練,要保證contentView內(nèi)部上下左右所有方向都有約束支撐娘香,設(shè)置不合理的話計(jì)算的高度就成了0苍狰。

這里還不得不提到一個(gè)UILabel的蛋疼問題,當(dāng)UILabel行數(shù)大于0時(shí)烘绽,需要指定preferredMaxLayoutWidth后它才知道自己什么時(shí)候該折行淋昭。這是個(gè)“雞生蛋蛋生雞”的問題,因?yàn)閁ILabel需要知道superview的寬度才能折行安接,而superview的寬度還依仗著子view寬度的累加才能確定翔忽。這個(gè)問題好像到iOS

8才能夠自動(dòng)解決(不過我們找到了解決方案)。

回到正題赫段,iOS 8 WWDC中推出了self-sizingcell的概念呀打,旨在讓cell自己負(fù)責(zé)自己的高度計(jì)算,使用frame layout和auto layout都可以享受到:

這個(gè)特性首先要求是iOS8糯笙,要是最低支持的系統(tǒng)版本小于8的話贬丛,還得針對(duì)老版本單寫套老式的算高(囧),不過用的API到不是新面孔:

self.tableView.estimatedRowHeight = 213;self.tableView.rowHeight = UITableViewAutomaticDimension;

這里又不得不吐槽了给涕,自動(dòng)計(jì)算rowHeight跟estimatedRowHeight到底是有什么仇豺憔,如果不加上估算高度的設(shè)置,自動(dòng)算高就失效了够庙。

PS:iOS8系統(tǒng)中rowHeight的默認(rèn)值已經(jīng)設(shè)置成了UITableViewAutomaticDimension恭应,所以第二行代碼可以省略。

問題:

這個(gè)自動(dòng)算高在push到下一個(gè)頁面或者轉(zhuǎn)屏?xí)r會(huì)出現(xiàn)高度特別詭異的情況耘眨,不過現(xiàn)在的版本修復(fù)了昼榛。

求一個(gè)能讓最低支持iOS8的公司。

iOS 8抽風(fēng)的算高機(jī)制

相同的代碼在iOS 7和iOS 8上滑動(dòng)順暢程度完全不同剔难,iOS 8莫名奇妙的卡胆屿。很大一部分原因是iOS8上的算高機(jī)制大不相同奥喻,這是我做的小測(cè)試:

研究后發(fā)現(xiàn)這么多次額外計(jì)算有下面的原因:

不開啟高度估算時(shí),UITableView上來就要對(duì)所有cell調(diào)用算高來確定contentSize非迹。

dequeueReusableCellWithIdentifier:forIndexPath:相比不帶“forIndexPath”的版本會(huì)多調(diào)用一次高度計(jì)算环鲤。

iOS7計(jì)算高度后有“緩存”機(jī)制,不會(huì)重復(fù)計(jì)算憎兽;而iOS 8不論何時(shí)都會(huì)重新計(jì)算cell高度冷离。

iOS8把高度計(jì)算搞成這個(gè)樣子,從WWDC也倒是能找到點(diǎn)解釋纯命,cell被認(rèn)為隨時(shí)都可能改變高度(如從設(shè)置中調(diào)整動(dòng)態(tài)字體大形靼),所以每次滑動(dòng)出來后都要重新計(jì)算高度亿汞。

說了這么多蔫耽,究竟有沒有既能省去算高煩惱,又能保證順暢的滑動(dòng)留夜,還能支持iOS 6+的一站式解決方案呢匙铡?

UITableView+FDTemplateLayoutCell

使用UITableView+FDTemplateLayoutCell無疑是解決算高問題的最佳實(shí)踐之一,既有iOS 8self-sizing功能簡(jiǎn)單的API碍粥,又可以達(dá)到iOS7流暢的滑動(dòng)效果鳖眼,還保持了最低支持iOS6。

使用起來大概是這樣:

#import - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {? ? return [tableView fd_heightForCellWithIdentifier:@"identifer" cacheByIndexPath:indexPath configuration:^(id cell) {? ? ? ? // 配置 cell 的數(shù)據(jù)源嚼摩,和 "cellForRow" 干的事一致钦讳,比如:? ? ? ? cell.entity = self.feedEntities[indexPath.row];? ? }];}

寫完上面的代碼后,你就已經(jīng)使用到了:

和每個(gè)UITableViewCellReuseID一一對(duì)應(yīng)的template layout cell

這個(gè)cell只為了參加高度計(jì)算枕面,不會(huì)真的顯示到屏幕上愿卒;它通過UITableView的-dequeueCellForReuseIdentifier:方法lazy創(chuàng)建并保存,所以要求這個(gè)ReuseID必須已經(jīng)被注冊(cè)到了UITableView中潮秘,也就是說琼开,要么是Storyboard中的原型cell,要么就是使用了UITableView的-registerClass:forCellReuseIdentifier:或-registerNib:forCellReuseIdentifier:其中之一的注冊(cè)方法枕荞。

根據(jù)autolayout約束自動(dòng)計(jì)算高度

使用了系統(tǒng)在iOS6就提供的API:-systemLayoutSizeFittingSize:

根據(jù)indexpath的一套高度緩存機(jī)制

計(jì)算出的高度會(huì)自動(dòng)進(jìn)行緩存嗅绰,所以滑動(dòng)時(shí)每個(gè)cell真正的高度計(jì)算只會(huì)發(fā)生一次挣跋,后面的高度詢問都會(huì)命中緩存,減少了非澈嫣可觀的多余計(jì)算谍夭。

自動(dòng)的緩存失效機(jī)制

無須擔(dān)心你數(shù)據(jù)源的變化引起的緩存失效讹挎,當(dāng)調(diào)用如-reloadData趴泌,-deleteRowsAtIndexPaths:withRowAnimation:等任何一個(gè)觸發(fā)

UITableView刷新機(jī)制的方法時(shí)倒戏,已有的高度緩存將以最小的代價(jià)執(zhí)行失效。如刪除一個(gè)indexPath為[0:5]的cell時(shí),[0:0]

~[0:4]的高度緩存不受影響碌嘀,而[0:5]后面所有的緩存值都向前移動(dòng)一個(gè)位置碾篡。自動(dòng)緩存失效機(jī)制對(duì)UITableView的9個(gè)公有API都進(jìn)行了分別的處理,以保證沒有一次多余的高度計(jì)算筏餐。

預(yù)緩存機(jī)制

預(yù)緩存機(jī)制將在UITableView沒有滑動(dòng)的空閑時(shí)刻執(zhí)行,計(jì)算和緩存那些還沒有顯示到屏幕中的cell牡拇,整個(gè)緩存過程完全沒有感知魁瞪,這使得完整列表的高度計(jì)算既沒有發(fā)生在加載時(shí),又沒有發(fā)生在滑動(dòng)時(shí)惠呼,同時(shí)保證了加載速度和滑動(dòng)流暢性导俘,下文會(huì)著重講下這塊的實(shí)現(xiàn)原理。

我們?cè)谠O(shè)計(jì)這個(gè)工具的API時(shí)斟酌了非常長(zhǎng)的時(shí)間剔蹋,既要保證功能的強(qiáng)大旅薄,也要保證接口的精簡(jiǎn),一行調(diào)用背后隱藏著很多功能泣崩。

這一套緩存機(jī)制能對(duì)滑動(dòng)起多大影響呢少梁?除了肉眼能明顯的感知到外,我還做了個(gè)小測(cè)試矫付。一個(gè)有54個(gè)內(nèi)容和高度不同cell的tableview凯沪,從頭滑動(dòng)到尾,再從尾滑動(dòng)到頭买优,iOS 8系統(tǒng)下妨马,iPhone 6,使用TimeProfiler監(jiān)測(cè)算高函數(shù)所花費(fèi)的時(shí)間:

未使用緩存API杀赢、未使用估算烘跺,共花費(fèi)877ms

使用緩存API、開啟估算脂崔,共花費(fèi)77ms

測(cè)試數(shù)據(jù)的精度先不管滤淳,從量級(jí)上就差了一個(gè)數(shù)量級(jí),說實(shí)話自己也沒想到差距有這么大砌左。

同時(shí)娇钱,工具也順手解決了-preferredMaxLayoutWidth的問題,在計(jì)算高度前向contentView加了一條和tableview寬度相同的寬度約束绊困,強(qiáng)行讓contentView內(nèi)部的控件知道了自己父view的寬度文搂,再反算自己被外界約束的寬度,破除“雞生蛋蛋生雞”的問題秤朗,這里比較tricky煤蹭,就不展開說了。下面說說利用RunLoop預(yù)緩存的實(shí)現(xiàn)。

利用RunLoop空閑時(shí)間執(zhí)行預(yù)緩存任務(wù)

FDTemplateLayoutCell的高度預(yù)緩存是一個(gè)優(yōu)化功能硝皂,它要求頁面處于空閑狀態(tài)時(shí)才執(zhí)行計(jì)算常挚,當(dāng)用戶正在滑動(dòng)列表時(shí)顯然不應(yīng)該執(zhí)行計(jì)算任務(wù)影響滑動(dòng)體驗(yàn)。

一般來說稽物,這個(gè)功能要耦合UITableView的滑動(dòng)狀態(tài)才行奄毡,但這種實(shí)現(xiàn)十分不優(yōu)雅且可能破壞外部的delegate結(jié)構(gòu),但好在我們還有RunLoop這個(gè)工具贝或,了解它的運(yùn)行機(jī)制后吼过,可以用很簡(jiǎn)單的代碼實(shí)現(xiàn)上面的功能。

空閑RunLoopMode

當(dāng)用戶正在滑動(dòng)UIScrollView時(shí)咪奖,RunLoop將切換到UITrackingRunLoopMode接受滑動(dòng)手勢(shì)和處理滑動(dòng)事件(包括減速和彈簧效果)盗忱,此時(shí),其他Mode(除NSRunLoopCommonModes這個(gè)組合Mode)下的事件將全部暫停執(zhí)行羊赵,來保證滑動(dòng)事件的優(yōu)先處理趟佃,這也是iOS滑動(dòng)順暢的重要原因。

當(dāng)UI沒在滑動(dòng)時(shí)昧捷,默認(rèn)的Mode是NSDefaultRunLoopMode(同CF中的kCFRunLoopDefaultMode)闲昭,同時(shí)也是CF中定義的“空閑狀態(tài)Mode”。當(dāng)用戶啥也不點(diǎn)靡挥,此時(shí)也沒有什么網(wǎng)絡(luò)IO時(shí)汤纸,就是在這個(gè)Mode下。

用RunLoopObserver找準(zhǔn)時(shí)機(jī)

注冊(cè)RunLoopObserver可以觀測(cè)當(dāng)前RunLoop的運(yùn)行狀態(tài)芹血,并在狀態(tài)機(jī)切換時(shí)收到通知:

RunLoop開始

RunLoop即將處理Timer

RunLoop即將處理Source

RunLoop即將進(jìn)入休眠狀態(tài)

RunLoop即將從休眠狀態(tài)被事件喚醒

RunLoop退出

因?yàn)椤邦A(yù)緩存高度”的任務(wù)需要在最無感知的時(shí)刻進(jìn)行贮泞,所以應(yīng)該同時(shí)滿足:

RunLoop處于“空閑”狀態(tài)Mode;

當(dāng)這一次RunLoop迭代處理完成了所有事件幔烛,馬上要休眠時(shí)啃擦。

使用CF的帶block版本的注冊(cè)函數(shù)可以讓代碼更簡(jiǎn)潔:

CFRunLoopRef runLoop = CFRunLoopGetCurrent();CFStringRef runLoopMode = kCFRunLoopDefaultMode;CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity _) {? ? // TODO here});CFRunLoopAddObserver(runLoop, observer, runLoopMode);

在其中的TODO位置,就可以開始任務(wù)的收集和分發(fā)了饿悬,當(dāng)然令蛉,不能忘記適時(shí)的移除這個(gè)observer。

分解成多個(gè)RunLoop Source任務(wù)

設(shè)列表有20個(gè)cell狡恬,加載后展示了前5個(gè)珠叔,那么開啟估算后tableview只計(jì)算了這5個(gè)的高度,此時(shí)剩下15個(gè)就是“預(yù)緩存”的任務(wù)弟劲,而我們并不希望這15個(gè)計(jì)算任務(wù)在同一個(gè)RunLoop迭代中同步執(zhí)行祷安,這樣會(huì)卡頓UI,所以應(yīng)該把它們分別分解到15個(gè)RunLoop迭代中執(zhí)行兔乞,這時(shí)就需要手動(dòng)向RunLoop中添加Source任務(wù)(由應(yīng)用發(fā)起和處理的是Source

0任務(wù))

Foundation層沒對(duì)RunLoopSource提供直接構(gòu)建的API汇鞭,但是提供了一個(gè)間接的凉唐、既熟悉又陌生的API:

- (void)performSelector:(SEL)aSelector? ? ? ? ? ? ? onThread:(NSThread *)thr? ? ? ? ? ? ? withObject:(id)arg? ? ? ? ? waitUntilDone:(BOOL)wait? ? ? ? ? ? ? ? ? modes:(NSArray *)array;

這個(gè)方法將創(chuàng)建一個(gè)Source0任務(wù),分發(fā)到指定線程的RunLoop中霍骄,在給定的Mode下執(zhí)行台囱,若指定的RunLoop處于休眠狀態(tài),則喚醒它處理事件读整,簡(jiǎn)單來說就是“睡你xx簿训,起來嗨!”

于是米间,我們用一個(gè)可變數(shù)組裝載當(dāng)前所有需要“預(yù)緩存”的indexpath强品,每個(gè)RunLoopObserver回調(diào)時(shí)都把第一個(gè)任務(wù)拿出來分發(fā):

NSMutableArray *mutableIndexPathsToBePrecached = self.fd_allIndexPathsToBePrecached.mutableCopy;CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity _) {? ? if (mutableIndexPathsToBePrecached.count == 0) {? ? ? ? CFRunLoopRemoveObserver(runLoop, observer, runLoopMode);? ? ? ? return;? ? }? ? NSIndexPath *indexPath = mutableIndexPathsToBePrecached.firstObject;? ? [mutableIndexPathsToBePrecached removeObject:indexPath];? ? [self performSelector:@selector(fd_precacheIndexPathIfNeeded:)? ? ? ? ? ? ? ? onThread:[NSThread mainThread]? ? ? ? ? ? ? withObject:indexPath? ? ? ? ? ? waitUntilDone:NO? ? ? ? ? ? ? ? ? ? modes:@[NSDefaultRunLoopMode]];});

這樣,每個(gè)任務(wù)都被分配到下個(gè)“空閑”RunLoop迭代中執(zhí)行车伞,其間但凡有滑動(dòng)事件開始,Mode切換成UITrackingRunLoopMode喻喳,所有的“預(yù)緩存”任務(wù)的分發(fā)和執(zhí)行都會(huì)自動(dòng)暫定另玖,最大程度保證滑動(dòng)流暢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末表伦,一起剝皮案震驚了整個(gè)濱河市谦去,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蹦哼,老刑警劉巖鳄哭,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異纲熏,居然都是意外死亡妆丘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門局劲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勺拣,“玉大人,你說我怎么就攤上這事鱼填∫┯校” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵苹丸,是天一觀的道長(zhǎng)愤惰。 經(jīng)常有香客問我,道長(zhǎng)赘理,這世上最難降的妖魔是什么宦言? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮商模,結(jié)果婚禮上蜡励,老公的妹妹穿的比我還像新娘令花。我一直安慰自己,他們只是感情好凉倚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布兼都。 她就那樣靜靜地躺著,像睡著了一般稽寒。 火紅的嫁衣襯著肌膚如雪扮碧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天杏糙,我揣著相機(jī)與錄音慎王,去河邊找鬼。 笑死宏侍,一個(gè)胖子當(dāng)著我的面吹牛赖淤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谅河,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼咱旱,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了绷耍?” 一聲冷哼從身側(cè)響起吐限,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎褂始,沒想到半個(gè)月后诸典,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡崎苗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年狐粱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胆数。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脑奠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出幅慌,到底是詐尸還是另有隱情宋欺,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布胰伍,位于F島的核電站齿诞,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏骂租。R本人自食惡果不足惜祷杈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望渗饮。 院中可真熱鬧但汞,春花似錦宿刮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至踩叭,卻和暖如春磕潮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背容贝。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國打工自脯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人斤富。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓膏潮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親满力。 傳聞我的和親對(duì)象是個(gè)殘疾皇子焕参,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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