一個iOS性能優(yōu)化組件

簡介

  • LNAsyncKit是一個異步渲染工具掏膏,它提供了便捷的方法幫助你將多個元素(Element)異步渲染到一張圖片上饰豺,讓這個過程代替UIKit的視圖構(gòu)建過程蜗侈,進而優(yōu)化App性能鲤拿;Prender提供預(yù)加載策略幫助你在Feed流中彌補異步渲染帶來的延時蚂四;除構(gòu)建視圖外沥阳,Transaction提供更優(yōu)雅的方式讓主線程與子線程交互跨琳,并能根據(jù)機器狀態(tài)控制并發(fā)數(shù)和主線程回調(diào)時機。

  • LNAsyncKit借(ji)鑒(cheng)了很多YYKit和Texture桐罕,如果對它們不是很了解可以戳這個比較詳細的文章脉让,這篇文章的作者是YY大神:iOS保持頁面流暢的技巧。流暢性優(yōu)化的思想基本上都如這篇文章所述功炮。

它可以提供哪些幫助

  • 還沒有找到方案優(yōu)化圓角溅潜、邊框、漸變的優(yōu)化方案薪伏,LNAsyncKit可以異步解決這些滚澜。
  • Feed流需要預(yù)加載策略,LNAsyncKit提供預(yù)加載區(qū)域計算方案(這個方案也用來預(yù)合成)嫁怀。
  • 提供一種與UIKit十分接近的方式構(gòu)建需要預(yù)合成的圖層设捐,讓你的復(fù)雜圖層構(gòu)建都放在子線程進行,且不會創(chuàng)建那么多UIView塘淑。
  • Demo展示了使用:AFNetworking/SDWebImage/IGListKit/YYModel/MJRefresh + LNAsyncKit搭建feed流的方法萝招。除去LNAsyncKit,前面5個構(gòu)成的這套體系已經(jīng)比較完整存捺,Demo中也提供了沒有使用LNAsyncKit構(gòu)建的Feed槐沼。因此,需要快速學(xué)習(xí)如何搭建一套Feed流的初學(xué)者可以參考這套三方捌治。

Github鏈接

你可以直接下載這個鏈接并運行上面的Demo參考代碼實現(xiàn)自己的異步列表岗钩,也可以直接使用Cocoapods??

pod 'LNAsyncKit'

流暢性優(yōu)化

網(wǎng)絡(luò)上已經(jīng)有了很多流暢性優(yōu)化的文章,再逐一復(fù)述這些優(yōu)化點意義不大肖油;這個文章是為了表達如何在Feed流中實現(xiàn)那些優(yōu)化思想兼吓,并把這個過程簡化;所以构韵,不再贅述這些優(yōu)化點為什么好周蹭、好多少趋艘,只談怎么實現(xiàn)它們;如果對這些優(yōu)化點有疑問可以參考上面鏈接的文章凶朗,以下這些觀點成立:

  • 圖層少的列表比圖層多的列表好瓷胧。
  • 沒有圓角、邊框棚愤、漸變等復(fù)雜圖層的比有的好搓萧。
  • 圖片尺寸和控件尺寸一樣大的好。
  • 模型解析放在子線程比放在主線程好宛畦。
  • 布局計算放在子線程比放在主線程好瘸洛。
  • 有預(yù)加載比沒有預(yù)加載好(見仁見智,也有喜歡無預(yù)加載列表的)次和。
  • Layer比View好(無手勢時)反肋。
  • 不透明圖層比透明圖層好。

在業(yè)務(wù)復(fù)雜度不變的前提下讓這些優(yōu)化工作變簡單踏施、自由就是LNAsyncKit的目標石蔗。

優(yōu)化一個Cell

我們將一個Cell視為Feed流的最小優(yōu)化單元,以一個Bilibili推薦Feed流中一個常規(guī)的Cell為例:

這樣一個小Cell中包含了:封面圖畅形、人數(shù)圖標养距、人數(shù)Label、主播昵稱日熬、直播間名棍厌、[直播]、直播內(nèi)容分類竖席、負反饋按鈕8個元素耘纱;除了這些元素外,還包括封面圖底部一個黑色漸變的圖層怕敬、[直播]的圓角揣炕、邊框和整個Cell的圓角(好像還有些陰影)帘皿;這個小Cell已經(jīng)包含比較多的小元素了东跪,我們在Demo中嘗試復(fù)原一下并查看視圖層級大致如下:

具體構(gòu)建代碼這里不贅述了,使用LNAsyncKit可以簡化這個Cell為如下這個樣子:

(右下角反饋Bug需要響應(yīng)事件鹰溜,通常這種控件會保持獨立)

以“直播”標簽為例虽填,視圖構(gòu)建方式區(qū)別如下:

UIKit:

    self.liveTagLabel.layer.cornerRadius = 3.f;
    self.liveTagLabel.layer.borderColor = [UIColor colorWithRed:239.f/255.f green:91.f/255.f blue:156.f/255.f alpha:1.f].CGColor;
    self.liveTagLabel.layer.borderWidth = 1.f;
    self.liveTagLabel.text = @"直播";
    self.liveTagLabel.font = [UIFont systemFontOfSize:12.f];
    self.liveTagLabel.textColor = [UIColor colorWithRed:239.f/255.f green:91.f/255.f blue:156.f/255.f alpha:1.f];
    self.liveTagLabel.textAlignment = NSTextAlignmentCenter;
    [self.cellContentView addSubview:self.liveTagLabel];

LNAsyncKit:

    LNAsyncTextElement *liveTagElement = [[LNAsyncTextElement alloc] init];
    liveTagElement.cornerRadius = 3.f;
    liveTagElement.borderColor = [UIColor colorWithRed:239.f/255.f green:91.f/255.f blue:156.f/255.f alpha:1.f];
    liveTagElement.borderWidth = 1.f;
    liveTagElement.text = @"直播";
    liveTagElement.font = [UIFont systemFontOfSize:12.f];
    liveTagElement.textColor = [UIColor colorWithRed:239.f/255.f green:91.f/255.f blue:156.f/255.f alpha:1.f];
    liveTagElement.textAligment = NSTextAlignmentCenter;
    [cellContentElement addSubElement:liveTagElement];

經(jīng)過LNAsyncKit渲染出與需要展示視圖面積一樣大的一張完整圖片,復(fù)雜渲染邏輯全部被子線程消化曹动,反饋到主線程只表現(xiàn)為一張與目標控件大小一致的圖片斋日。

原理

與UIKit類似,LNAsyncKit也使用視圖樹構(gòu)建最終視圖。區(qū)別是:

A. Element繼承自NSObject墓陈,這些Element可以在子線程創(chuàng)建恶守、渲染第献、銷毀⊥酶郏可以將Element理解為“一個需要繪制圖層”的描述物庸毫,它并不是一個實體,它與UIView/CALayer的區(qū)別就好像:UIView是你要買的一件物品衫樊;Element則是下單信息飒赃,里面包含這件物品的各種描述信息,多大科侈、什么顏色等载佳。

B. 所有的Element都是臨時的,這些信息在構(gòu)建出結(jié)果后就會被銷毀臀栈,你可以在進入子線程之后創(chuàng)建這些Element蔫慧,在渲染出真正的圖片后銷毀這些Element,然后在主線程返回需要的圖片权薯,像這樣:

    dispatch_queue_t queue = dispatch_queue_create(0, 0);
    dispatch_async(queue, ^{
        LNAsyncElement *contentElement = [weakSelf rebuildElements];
        [LNAsyncRenderer traversalElement:contentElement];
        UIImage *image = contentElement.renderResult;
        contentElement.renderResult = nil;
        dispatch_async(dispatch_get_main_queue(), ^{
            weakSelf.imageView.image = image;
        });
    });

rebuildElement的過程可以構(gòu)建出很復(fù)雜的一棵樹藕漱,但對主線程來說,這并不會造成問題崭闲!不在主線程出現(xiàn)Element也是LNAsyncKit推薦的使用方法(拿到resultImage后肋联,把Element.resultImage置為空),當然刁俭,出現(xiàn)了一般也無所謂橄仍,NSObject的消耗相對于UIView來講是很小的。

C.Element是逐層渲染的:實際上是后續(xù)遍歷牍戚,把A的子Element先渲染出來侮繁,然后渲染A,再把A當做一個子節(jié)點渲染父節(jié)點如孝,LNAsyncRendererTraversalStack就是遍歷時使用的棧宪哩、LNAsyncRenderer.traversal函數(shù)是遍歷方法。遍歷中自帶了環(huán)檢測第晰,不會渲染重復(fù)Element锁孟,像這樣:

    LNAsyncRendererTraversalStack *stack = [[LNAsyncRendererTraversalStack alloc] init];
    [stack pushElements:@[element]];
    
    NSMutableSet <LNAsyncElement *> *repeatDetectMSet = [[NSMutableSet alloc] init];
    while (!stack.isEmpty) {
        LNAsyncElement *topElement = [stack top];
        if (topElement.getSubElements.count > 0 && (![repeatDetectMSet containsObject:topElement])) {
            [repeatDetectMSet addObject:topElement];
            [stack pushElements:topElement.getSubElements.reverseObjectEnumerator.allObjects];
        } else {
            [stack pop];
            [self renderElement:topElement];
            for (LNAsyncElement *subElement in topElement.getSubElements) {
                subElement.renderResult = nil;
            }
        }
    }

LNAsync自帶了一些Element:

  • LNAsyncElement: 對應(yīng)于UIKit的UIView,是其他Element的基類茁瘦,包含了背景色品抽、frame、和常用的邊界甜熔、圓角等屬性圆恤。
  • LNASyncImageElement: 對應(yīng)于UIImageView,渲染一張圖片腔稀、提供三種填充方式盆昙。
  • LNAsyncTextElement: 對應(yīng)于UILabel羽历,渲染一段文字,提供常規(guī)文字屬性淡喜、支持折行窄陡。
  • LNAsyncLinerGradientElement: 對應(yīng)于CAGradientLayer,渲染一段漸變色拆火。

自定義Element:

除原生Element外跳夭,我們也推薦封裝自己的Element,例如:一個AvatarElement们镜,可以將用戶頭像币叹、VIP標識、頭像邊框等修飾物渲染在一起模狭,重寫- (void)renderSelfWithContext:(CGContextRef)context颈抚,在這個方法中分別繪制這三個元素。

自定義Element的意義在于嚼鹉,所有自定義過的Element都是可復(fù)用贩汉、可組合的,這樣方便保持整個App風(fēng)格統(tǒng)一锚赤,也會適當減少開發(fā)成本匹舞。

Feed流

上面我們已經(jīng)介紹過單個Cell、單張圖片如何異步渲染以優(yōu)化性能线脚,但性能問題往往不是單張圖片所能引發(fā)赐稽,LNAsyncKit更傾向于性能敏感的場景:Feed流;渲染Feed流相比渲染單個視圖需要考慮的事情要多一些:Cell復(fù)用浑侥、渲染好的圖片緩存姊舵、多張圖片下載和結(jié)果合并等問題;除此之外寓落,也考慮使用預(yù)加載括丁、預(yù)渲染功能來優(yōu)化用戶體驗。

使用到的三方庫:

  • AFNetworking 網(wǎng)絡(luò)
  • IGListKit Feed流框架伶选,可以拆分各個模塊業(yè)務(wù)
  • SDWebImage 圖片下載
  • YYModel 字典轉(zhuǎn)模型
  • MJRefresh 上拉/下拉刷新組件
  • 一位大佬寫的免費API 史飞,雖然我不認識這位大佬,但這些接口確實非常方便考蕾,在這里面朝空氣感謝一下~

這些都是非常成熟的三方框架祸憋,直接拿來用會減少不少開發(fā)時間会宪;這里主要是介紹如何將LNAsyncKit融進這個體系中去肖卧。Demo中已經(jīng)提供了默認的Feed流和異步的Feed流代碼,如果遇到了一些奇怪的Bug可以參考Demo中的實現(xiàn)掸鹅,目前這兩個Demo都可以正常運行塞帐。

  • 默認Demo:我們用此Demo展示一個常規(guī)Feed流實現(xiàn)過程拦赠,沒有使用任何修飾手法或設(shè)計思想,可以理解為實現(xiàn)一個Feed流所需要做的最少工作葵姥。
  • 異步Demo:我們用此Demo將使用LNAsyncKit實現(xiàn)Feed流時與通常情況下的實現(xiàn)的進行對比荷鼠,了解從普通Feed轉(zhuǎn)異步Feed的修改點和差異之處。

默認Feed流實現(xiàn):

  1. ViewDidLoad中使用AFNetworking請求一頁數(shù)據(jù)榔幸,使用YYModel解析成Model類型數(shù)據(jù)允乐,賦值給VC。
  2. VC調(diào)用CollectionView/IGList刷新列表削咆,將Model賦值到Cell內(nèi)部牍疏。
  3. Cell內(nèi)部賦值懶加載的Label、ImageView調(diào)用sd_setImage下載圖片展示拨齐。

異步Feed流的優(yōu)化:

1.圖片下載放在Model中進行

A.因為異步Feed不僅僅需要下載圖片鳞陨,也需要將多個原始圖片進行預(yù)合成,所以這個過程在Model中進行可以保證不會因Cell復(fù)用問題導(dǎo)致同一時間合成多次瞻惋,如果你在Cell中異步進行圖層合成厦滤,那可能每次賦值Model都會合成一次,但在Model中合成后可以一直存放在Model中(Model只持有弱引用歼狼,存在全局的NSCache中)掏导。

B.考慮預(yù)加載,我們認為圖層的預(yù)加載和預(yù)合成是兩種優(yōu)先級的事情羽峰,通常距離屏幕焦點區(qū)域較遠的區(qū)域只需要進行圖片預(yù)下載碘菜,而距離較近的地方則需要預(yù)合成,不論是哪種方式限寞,Cell通常只會在展示在屏幕上的時間點附近才能拿到忍啸,如果圖片下載放在Cell中進行,是很難實現(xiàn)“預(yù)”的履植。

MVC中Model的職責之一是提供View展示需要的數(shù)據(jù)计雌,所以在Model中下載圖片并非錯誤或不恰當?shù)淖龇ā?/p>

2.模型解析和布局計算視為網(wǎng)絡(luò)請求的一部分

通常,在使用AFNetworking進行網(wǎng)絡(luò)請求時玫霎,我們通常在成功回調(diào)中進行模型解析和列表刷新凿滤,列表刷新時走CollectionView的dataSource協(xié)議計算布局。

異步列表不推薦這樣做:模型解析的過程沒有想象中的那樣簡單庶近,通常進行模型解析時需要逐層遍歷Dictionary翁脆,然后創(chuàng)建大量Model和子Model,雖然單個NSObject開銷不大鼻种,但列表視圖的模型總是堆積起來的反番,創(chuàng)建如此多的對象也是個不小的開銷。

計算布局的耗時是公認的,所以一般表視圖優(yōu)化都推薦緩存行高罢缸,但即便緩存行高篙贸,第一次在主線程中的計算也是有一定耗時的。

我們推薦在AFNetworking回調(diào)中異步進行模型解析和布局計算枫疆,將這兩個操作視為網(wǎng)絡(luò)請求的一部分爵川,這并不會對網(wǎng)絡(luò)請求的整體響應(yīng)時間有較大的影響,因為網(wǎng)絡(luò)回調(diào)時間單位通常要比屏幕刷新時間單位高出一個數(shù)量級息楔。況且寝贡,預(yù)加載技術(shù)完全可以彌補這段小延時。

在請求回調(diào)中賦值給Model的LayoutObj就是對這個過程的封裝值依,像這樣:

- (void)transferFeedData:(NSDictionary *)dic comletion:(DemoFeedNetworkCompletionBlock)completion
{
    LNAsyncTransaction *transaction = [[LNAsyncTransaction alloc] init];
    
    [transaction addOperationWithBlock:^id _Nullable{
        DemoFeedModel *feedModel = [DemoFeedModel yy_modelWithDictionary:dic];
        for (DemoFeedItemModel *item in feedModel.result) {
            DemoAsyncFeedDisplayLayoutObjInput *layoutInput = [[DemoAsyncFeedDisplayLayoutObjInput alloc] init];
            layoutInput.contextString = item.title;
            layoutInput.hwScale = 0.3f + ((random()%100)/100.f)*0.5f; 
            DemoAsyncFeedDisplayLayoutObj *layoutObj = [[DemoAsyncFeedDisplayLayoutObj alloc] initWithInput:layoutInput];
            item.layoutObj = layoutObj;
        }
        return feedModel;
    } priority:1 queue:_transferQueue completion:^(id  _Nullable value, BOOL canceled) {
        if (completion) {
            completion(YES, value, nil);
        }
    }];
    
    [transaction commit];
}
3.在Model中布局

這聽起來有點詭異兔甘,在Model中下載圖片也就算了,為什么視圖操作也在Model中進行鳞滨?

我們已經(jīng)解釋了Element的職責洞焙,它只是負責描述的類。使用element構(gòu)建視圖的過程就是:Model想好要怎么構(gòu)建(Element)拯啦,把想法交付LNAsyncRenderer澡匪,renderer交付我們image,Model把image反回給View顯示出來褒链。就像我們在開始的時候講述的那樣唁情。

4.預(yù)加載

預(yù)加載主要內(nèi)容包括兩個方面:預(yù)加載下一頁信息和預(yù)加載圖片。這里提到的預(yù)加載主要是指預(yù)加載圖片:

根據(jù)上面我們提到了圖片加載都是在Model中進行的甫匹,所以甸鸟,每個Model都需要一個必要的參數(shù)來標記自身所持有的資源已經(jīng)到了那種緊急的程度了,如果距離當前用戶焦點還很遠兵迅,說明自己的資源目前不是很緊急抢韭,可以先靜觀其變;如果距離用戶焦點有點近了恍箭,說明自己可能需要開始考慮先把圖片下載下來刻恭;如果距離用戶焦點已經(jīng)相當近了,就要立刻開始準備把已有資源預(yù)合成了扯夭。類似這樣:

- (void)setStatus:(DemoFeedItemModelStatus)status
{
    if (status > _status) {
        _status = status;
    }
    [self checkCurrentStatus];
}

- (void)checkCurrentStatus
{
    if (self.status >= DemoFeedItemModelStatusPreload) {
        //需要預(yù)加載圖片
        [self preloadImage];
    }
    
    if (self.status >= DemoFeedItemModelStatusDisplay) {
         //需要渲染視圖
         [self renderView];
    }
}

LNAsyncCollectionViewPrender提供了一套資源緊急程度標記策略鳍贾,將距離當前屏幕中心較遠的資源標記為不緊急,較近的資源標記為緊急交洗,Model受緊急程度標記影響自主進行預(yù)加載或預(yù)渲染骑科。

這套智能預(yù)加載機制來自于Texture,非常的實用构拳,我將它修改為Objective-C實現(xiàn)咆爽,并做了簡化處理梁棠。你甚至可以參照這個區(qū)間計算思路制作一個滾動列表曝光打點類,來計算那些更符合用戶視距的曝光區(qū)間伍掀,而不是僅僅簡單依賴cell/View的生命周期掰茶,說到這不得不提一嘴:我曾經(jīng)見過一個埋點系統(tǒng)迭代了兩年多依然沒啥卵用暇藏。

5.圖片一致性校驗

異步Cell渲染圖片回調(diào)設(shè)置圖片需要進行渲染的模型與當前模型是否一致的校驗蜜笤,復(fù)用可能會導(dǎo)致一個Cell先后被設(shè)置兩個Model,這樣兩個Model在異步渲染結(jié)束后都可能通知Cell刷新數(shù)據(jù)盐碱,所以需要一致性校驗把兔。同步不存在這個問題,后來的內(nèi)容總是會覆蓋掉先來的圖片瓮顽。像這樣:

    NSObject *model = self.model;
    __weak DemoAsyncFeedCell *weakSelf = self;
    [self.model demoAsyncFeedItemLoadRenderImage:^(BOOL isCanceled, UIImage * _Nullable resultImage) {
        if (!isCanceled && resultImage && model == weakSelf.model) {
            weakSelf.contentView.layer.contents = (__bridge id)resultImage.CGImage;
        }
    }];
6.渲染緩存

與SDWebImage下載的原生Image不同县好,渲染后的圖片存儲在額外的一個渲染緩存中,Model弱引用持有暖混,緩存內(nèi)部使用LRU管理缕贡;不能使用Model強引用,因為有些Feed流是常駐的拣播,我們不希望內(nèi)存浪費在不是主要消費場景的常駐頁面中晾咪。LNAsyncCache是統(tǒng)一的存放的地方,你可以在渲染成功后把圖片存在這里贮配,使用弱指針指向它谍倦,如果被刪除了,就重新渲染泪勒、存儲昼蛀。

7.減少渲染次數(shù)

SD下載圖片時附帶AvoidDecode參數(shù),因為合成過程會將Image渲染到一塊內(nèi)存中圆存,這個過程本身就包含了解碼叼旋,且也是在子線程中進行;使用這個參數(shù)可以減少圖片剛下載好時的那次渲染沦辙。像這樣:

[[SDWebImageManager sharedManager] loadImageWithURL:[NSURL URLWithString:weakSelf.image]
                                            options:SDWebImageAvoidDecodeImage 
                                           progress:nil 
                                          completed:nil];

總結(jié)

LNAsyncKit優(yōu)化的內(nèi)容就如上所述:

  • 從主線程的角度來看:除了刷新CollectionView和計算預(yù)加載區(qū)域外基本上沒有耗時工作送淆,布局計算和模型解析轉(zhuǎn)移到了子線程統(tǒng)一進行,Element創(chuàng)建銷毀操作主線程基本上沒有感知怕轿。
  • 從CPU的角度來看:圓角偷崩、邊框、漸變等工作都在圖層合成的時候異步消化了撞羽,返回的圖片大小和Layer控件大小也是一致的阐斜,圖層的復(fù)雜層級也被子線程異步消化。
  • 從子線程角度看:子線程有很多诀紊。

寫異步Feed流比普通Feed流難度要稍微大一些谒出,平均開發(fā)的時間成本也會有所上升;從效率上來講,每個需求的開發(fā)效率確實降低了笤喳,但這將會省去在未來單獨成立一個性能優(yōu)化小組進行優(yōu)化的效率要高得多为居。平臺類型的開發(fā)人員往往沒有業(yè)務(wù)開發(fā)對業(yè)務(wù)更熟悉,因此需要頻繁交流確認優(yōu)化點杀狡、改動范圍蒙畴、影響等等。而且呜象,有時遇到優(yōu)化點時業(yè)務(wù)受限膳凝,可能不敢大刀闊斧地糾正,導(dǎo)致優(yōu)化后的結(jié)果和優(yōu)化前對比并不明顯恭陡。LNAsyncKit讓業(yè)務(wù)線從一開始做需求時就考慮到優(yōu)化內(nèi)容蹬音,從而省去了專項優(yōu)化的時間。當然休玩,如果App整體不考慮性能問題著淆,選擇正常的開發(fā)方式就好。

雜談

iPhone手機硬件越來越強拴疤,常規(guī)業(yè)務(wù)不進行優(yōu)化一般也能達到流暢性標準永部,端內(nèi)的卡頓只要不是特別嚴重產(chǎn)品經(jīng)理通常也都能接受;我在需求中使用了類似的方式進行性能優(yōu)化遥赚,開發(fā)時間確實很緊扬舒。當然,如果你的公司只考慮需求產(chǎn)出凫佛,他們通常不會給你這些時間讲坎,你可以在自己的編碼追求和實際情況之間決定是否要額外做這些事。

LNAsyncKit可以直接使用愧薛,也可以將它當做你更深層次了解性能優(yōu)化晨炕、Texture的墊腳石;總之毫炉,它能起到任何幫助瓮栗,我都將十分榮幸。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瞄勾,一起剝皮案震驚了整個濱河市费奸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌进陡,老刑警劉巖愿阐,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異趾疚,居然都是意外死亡缨历,警方通過查閱死者的電腦和手機以蕴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辛孵,“玉大人丛肮,你說我怎么就攤上這事∑歉浚” “怎么了宝与?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鲜滩。 經(jīng)常有香客問我伴鳖,道長节值,這世上最難降的妖魔是什么徙硅? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮搞疗,結(jié)果婚禮上嗓蘑,老公的妹妹穿的比我還像新娘。我一直安慰自己匿乃,他們只是感情好桩皿,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著幢炸,像睡著了一般泄隔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宛徊,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天佛嬉,我揣著相機與錄音,去河邊找鬼闸天。 笑死暖呕,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的苞氮。 我是一名探鬼主播湾揽,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼笼吟!你這毒婦竟也來了库物?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤贷帮,失蹤者是張志新(化名)和其女友劉穎戚揭,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體皿桑,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡毫目,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年蔬啡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镀虐。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡箱蟆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出刮便,到底是詐尸還是另有隱情空猜,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布恨旱,位于F島的核電站辈毯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏搜贤。R本人自食惡果不足惜谆沃,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望仪芒。 院中可真熱鬧唁影,春花似錦、人聲如沸掂名。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饺蔑。三九已至锌介,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間猾警,已是汗流浹背孔祸。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留肿嘲,地道東北人融击。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像雳窟,于是被迫代替她去往敵國和親尊浪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

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