iOS 渲染小結(jié)

本文整理一下有關(guān)計(jì)算機(jī)圖像渲染流程,以及 iOS 渲染相關(guān)知識,最后介紹一下在 iOS 開發(fā)過程中保持 APP 流暢的注意事項(xiàng)。

簡介

在顯示器上顯示的圖像是由一幀一幀的畫面組成的,當(dāng)一幀畫面繪制完成后,準(zhǔn)備畫下一幀,顯示器會發(fā)出一個垂直同步信號 VSync(vertical synchronization)刷新畫面。顯示器通常以固定頻率進(jìn)行刷新,這個刷新率就是 VSync 信號產(chǎn)生的頻率。
計(jì)算機(jī)通過 CPU、GPU、顯示器協(xié)同工作顯示圖像。CPU 計(jì)算好顯示內(nèi)容提交到 GPU,GPU 渲染完成后將渲染結(jié)果放入幀緩沖區(qū),隨后視頻控制器會按照 VSync 信號逐行讀取幀緩沖區(qū)的數(shù)據(jù),經(jīng)過可能的數(shù)模轉(zhuǎn)換傳遞給顯示器顯示。

計(jì)算機(jī)將存儲在內(nèi)存中的形狀轉(zhuǎn)換成實(shí)際繪制在屏幕上的圖像的過程稱為渲染( Render )。下面就來看一下渲染的過程。

計(jì)算機(jī)圖像渲染

圖像渲染流程,大概的步驟:

Application 應(yīng)用處理階段:得到圖元

這個階段圖像在應(yīng)用中被處理,可能會對圖像進(jìn)行一系列的操作或者改變,此時(shí)還處于 CPU 負(fù)責(zé)的時(shí)期。最終將新的圖像信息傳給下一階段。這部分信息被叫做圖元(primitives)用于表示渲染的頂點(diǎn)數(shù)據(jù),如:點(diǎn)、線、三角形。

Geometry 幾何處理階段:處理圖元

進(jìn)入這個階段之后,就主要由 GPU 負(fù)責(zé)了。GPU 拿到上一個階段傳遞下來的圖元信息,對這部分圖元進(jìn)行處理,之后輸出新的圖元。這一系列階段包括:

  • 頂點(diǎn)著色器(Vertex Shader):將圖元中的頂點(diǎn)信息進(jìn)行處理,主要的目的是把 3D 坐標(biāo)轉(zhuǎn)為另一種 3D 坐標(biāo),同時(shí)也可以對頂點(diǎn)屬性進(jìn)行一些基本處理。
  • 形狀裝配(Shape Assembly):將圖元中的三角形、線段、點(diǎn)分別對應(yīng)的頂點(diǎn) Vertex 裝配成指定圖元的形狀。
  • 幾何著色器(Geometry Shader):產(chǎn)生額外的頂點(diǎn) Vertex,將原始圖元轉(zhuǎn)換成新圖元,以構(gòu)建其他形狀的模型。簡單來說就是基于通過三角形、線段和點(diǎn)構(gòu)建更復(fù)雜的幾何圖形。

Rasterization 光柵化階段:圖元轉(zhuǎn)換為像素

光柵化的主要目的是將幾何渲染之后的圖元信息數(shù)據(jù),轉(zhuǎn)換為一系列的像素,以便后續(xù)顯示在屏幕上。根據(jù)圖元信息,計(jì)算出每個圖元所覆蓋的像素信息,生成片段。片段(Fragment) 是渲染一個像素所需要的所有數(shù)據(jù)。

Pixel 像素處理階段:處理像素,得到位圖

經(jīng)過上述光柵化階段,我們得到了圖元所對應(yīng)的像素,此時(shí),我們需要給這些像素填充顏色和效果,只要有足夠多的不同色彩的像素,就可以制作出色彩豐富的圖象。所以最后這個階段就是給像素填充正確的內(nèi)容,最終顯示在屏幕上。這些經(jīng)過處理、蘊(yùn)含大量信息的像素點(diǎn)集合,被稱作位圖(bitmap)。也就是說,Pixel 階段最終輸出的結(jié)果就是位圖,過程具體包含:

  • 片段著色器(Fragment Shader):也叫做 Pixel Shader,這個階段的目的是給每一個像素 Pixel 賦予正確的顏色。顏色的來源就是之前得到的頂點(diǎn)、紋理、光照等信息。由于需要處理紋理、光照等復(fù)雜信息,所以這通常是整個系統(tǒng)的性能瓶頸。
  • 測試與混合(Tests and Blending):也叫做 Merging 階段,這個階段主要處理片段的前后位置以及透明度。會檢測各個著色片段的深度值 z 坐標(biāo),從而判斷片段的前后位置,以及是否應(yīng)該被舍棄。同時(shí)也會計(jì)算相應(yīng)的透明度 alpha 值,從而進(jìn)行片段的混合,得到最終的顏色。

圖像渲染流程結(jié)束之后,接下來就需要將得到的像素信息顯示在物理屏幕上了。GPU 最后一步渲染結(jié)束之后像素信息,被存在幀緩沖器(Framebuffer)中,之后視頻控制器(Video Controller)會讀取幀緩沖器中的信息,經(jīng)過數(shù)模轉(zhuǎn)換傳遞給顯示器進(jìn)行顯示。

iOS 中的渲染

iOS 的渲染框架依然符合渲染流水線的基本架構(gòu)。在硬件基礎(chǔ)之上,iOS 中有 Core Graphics、Core Animation、Core Image、OpenGL 等多種軟件框架來繪制內(nèi)容,在 CPU 與 GPU 之間進(jìn)行了更高層地封裝。

UIKit 是 iOS 開發(fā)者最常用的框架,可以通過設(shè)置 UIKit 組件的布局以及相關(guān)屬性來繪制界面。顯示、動畫都通過 CoreAnimation,依賴于 OpenGL ES、Metal 做 GPU 渲染,CoreGraphics 做 CPU 渲染,最底層的 GraphicsHardWare 是圖形硬件。

顯示在屏幕上的 UIView 繼承自 UIResponder 自身并不具備在屏幕成像的能力,其主要負(fù)責(zé)對用戶操作事件的響應(yīng)。我們看到的屏幕上的內(nèi)容都由 CALayer 進(jìn)行管理,CALayer 中有個屬性 contents 提供了 layer 的內(nèi)容。contents 屬性保存了由設(shè)備渲染流水線渲染好的位圖 bitmap(通常也被稱為 backing store),而當(dāng)設(shè)備屏幕進(jìn)行刷新時(shí),會從 CALayer 中讀取生成好的 bitmap,進(jìn)而呈現(xiàn)到屏幕上。每次渲染 需要重繪時(shí),Core Animation 會觸發(fā)調(diào)用 drawRect: 方法,使用存儲好的 bitmap 進(jìn)行新一輪的展示。

  • Core Animation 主要職責(zé)包含:渲染、構(gòu)建和實(shí)現(xiàn)動畫。
    通常我們會使用 Core Animation 來高效、方便地實(shí)現(xiàn)動畫,動畫實(shí)現(xiàn)只是它功能中的一部分。除此之外其職責(zé)就是盡可能快地組合屏幕上不同的可視內(nèi)容,這個內(nèi)容是被分解成獨(dú)立的 layer,這些圖層會被存儲在一個叫做圖層樹的體系之中。從本質(zhì)上而言,CALayer 是用戶所能在屏幕上看見的一切的基礎(chǔ)。

  • Core Graphics 是一個強(qiáng)大的二維圖像繪制引擎,是 iOS 的核心圖形庫,常用的比如 CGRect 就定義在這個框架下。

  • Core Image 是一個高性能的圖像處理分析的框架,支持CPU、GPU兩種處理模式。它擁有一系列現(xiàn)成的圖像濾鏡,能對已存在的圖像進(jìn)行高效的處理。

  • OpenGL ES(OpenGL for Embedded Systems,簡稱 GLES),是 OpenGL 的子集。是一個提供了 2D 和 3D 圖形渲染的 API,它能和 GPU 密切的配合,最高效地利用 GPU 的能力,實(shí)現(xiàn)硬件加速渲染。 OpenGL 是一套第三方標(biāo)準(zhǔn),函數(shù)的內(nèi)部實(shí)現(xiàn)由對應(yīng)的 GPU 廠商開發(fā)實(shí)現(xiàn)。

  • Metal 類似于 OpenGL ES,也是一套第三方標(biāo)準(zhǔn),具體實(shí)現(xiàn)由蘋果實(shí)現(xiàn)。Core Animation、Core Image、SceneKit、SpriteKit 等等渲染框架都是構(gòu)建于 Metal 之上的。

渲染過程

iOS 中 APP 的渲染是由一個獨(dú)立的進(jìn)程 Render Server 負(fù)責(zé)。APP 將渲染任務(wù)及相關(guān)數(shù)據(jù)提交給 Render Server。Render Server 處理完數(shù)據(jù)后,再傳遞至 GPU。最后由 GPU 調(diào)用 iOS 的圖像設(shè)備進(jìn)行顯示。
1、CoreAnimation 提交會話,包括自己和子樹(view hierarchy)的 layout 狀態(tài)等;
2、RenderServer 解析提交的子樹狀態(tài),生成繪制指令;
3、GPU執(zhí)行繪制指令;
4、顯示渲染后的數(shù)據(jù);

上面的 Commit Transaction 其實(shí)可以細(xì)分為 4 個步驟: Layout、Display、Prepare、Commit

  • Layout 階段主要進(jìn)行視圖構(gòu)建,包括:layoutSubviews 方法的重載,addSubview: 方法添加子視圖等。
  • Display 階段主要進(jìn)行視圖繪制,這里僅僅是設(shè)置最要成像的圖元數(shù)據(jù)。重載視圖的 drawRect: 方法可以自定義 UIView 的顯示,其原理是在 drawRect: 方法內(nèi)部繪制寄宿圖,該過程使用 CPU 和內(nèi)存。
  • Prepare 階段屬于附加步驟,一般處理圖像的解碼和轉(zhuǎn)換等操作。
  • Commit 階段主要將圖層進(jìn)行打包,并將它們發(fā)送至 Render Server。該過程會遞歸執(zhí)行,因?yàn)閳D層和視圖都是以樹形結(jié)構(gòu)存在,如果子樹太復(fù)雜,會消耗很大,對性能造成影響。

Tile-Based 渲染

Tiled-Based 渲染是移動設(shè)備的主流。整個屏幕會分解成N*Npixels組成的瓦片(Tiles),tiles存儲于SoC 緩存中。對于每一塊 tile,把必須的幾何體提交到 OpenGL ES,然后進(jìn)行渲染(光柵化)。完畢后,將 tile 的數(shù)據(jù)發(fā)送回 CPU。

普通的Tile-Based渲染流程
1、CommandBuffer,接受 OpenGL ES 處理完畢的渲染指令;
2、Tiler,調(diào)用頂點(diǎn)著色器,把頂點(diǎn)數(shù)據(jù)進(jìn)行分塊(Tiling);
3、ParameterBuffer,接受分塊完畢的tile和對應(yīng)的渲染參數(shù);
4、Renderer,調(diào)用片元著色器,進(jìn)行像素渲染,處理得到 bitmap,之后存入 Render Buffer;
5、RenderBuffer,存儲渲染完畢的像素,供之后的 Display 操作使用;

離屏渲染

普通情況下 GPU 直接將渲染好的內(nèi)容放入 Framebuffer 中,而離屏渲染需要先額外創(chuàng)建離屏渲染緩沖區(qū) Offscreen Buffer,將提前渲染好的內(nèi)容放入其中,等到合適的時(shí)機(jī)再將 Offscreen Buffer 中的內(nèi)容進(jìn)一步疊加、渲染,完成后將結(jié)果切換到 Framebuffer 中。

離屏渲染時(shí)由于 APP 需要提前對部分內(nèi)容進(jìn)行額外的渲染并保存到 Offscreen Buffer,以及需要在必要時(shí)刻對 Offscreen Buffer 和 Framebuffer 進(jìn)行內(nèi)容切換,所以會需要更長的處理時(shí)間。并且 Offscreen Buffer 本身就需要額外的空間,大量的離屏渲染可能早能內(nèi)存的過大壓力。
可見離屏渲染的開銷非常大,一旦需要離屏渲染的內(nèi)容過多,很容易造成掉幀的問題。所以盡量避免離屏渲染。

使用離屏渲染原因:
1、一些特殊效果需要使用額外的 Offscreen Buffer 來保存渲染的中間狀態(tài),所以不得不使用離屏渲染。比如陰影、圓角等等。
2、處于效率目的,可以將內(nèi)容提前渲染保存在 Offscreen Buffer 中,達(dá)到復(fù)用的目的。

觸發(fā)離屏渲染的情況:
1、使用了 layer.mask
遮罩顯示的內(nèi)容是由兩層渲染結(jié)果疊加,所以必須要利用額外的內(nèi)存空間對中間的渲染結(jié)果進(jìn)行保存,因此系統(tǒng)會默認(rèn)觸發(fā)離屏渲染。

2、模糊特效 UIBlurEffectView
模糊過程分為多步:先渲染需要模糊的內(nèi)容本身,然后對內(nèi)容進(jìn)行縮放,然后分別對內(nèi)容進(jìn)行橫縱方向的模糊操作,最后一步用模糊后的結(jié)果疊加合成,最終實(shí)現(xiàn)完整的模糊特效。
使用 UIBlurEffectView ,應(yīng)該是盡可能小的 view,因?yàn)樾阅芟木薮蟆?/p>

3、光柵化的 layer.shouldRasterize
把視圖的內(nèi)容渲染成紋理并緩存,可以通過CALayer的shouldRasterize屬性開啟光柵化。
注意,光柵化的元素,總大小限制為2.5倍的屏幕。
更新內(nèi)容時(shí),會啟用離屏渲染,所以更新代價(jià)較大,只能用于靜態(tài)內(nèi)容;而且如果光柵化的元素100ms 沒有被使用將被移除,故而不常用元素的光柵化并不會優(yōu)化顯示。
圓角、陰影、組透明度等會由系統(tǒng)自動觸發(fā)離屏渲染,那么打開光柵化可以節(jié)約第二次及以后的渲染時(shí)間。而多層 subLayer 的情況由于不會自動觸發(fā)離屏渲染,所以相比之下會多花費(fèi)第一次離屏渲染的時(shí)間,但是可以節(jié)約后續(xù)的重復(fù)渲染的開銷。

4、組透明度 layer.allowsGroupOpacity / layer.opacity
CALayer 的 allowsGroupOpacity 屬性,UIView 的 alpha 屬性等,同于 CALayer opacity 屬性。
allowsGroupOpacity = YES,子 layer 在視覺上的透明度的上限是其父 layer 的 opacity。當(dāng)父視圖的layer.opacity != 1.0時(shí),會開啟離屏渲染。layer.opacity == 1.0時(shí),父視圖不用管子視圖,只需顯示當(dāng)前視圖即可。

5、需要進(jìn)行裁剪的 layer ,layer.masksToBounds / view.clipsToBounds
設(shè)置 cornerRadius 剪裁圓角時(shí),沒有設(shè)置 masksToBounds = YES,由于不需要疊加裁剪,此時(shí)是并不會觸發(fā)離屏渲染的。而當(dāng)設(shè)置了裁剪屬性的時(shí)候,由于 masksToBounds 會對 layer 以及所有 subLayer 的 content 都進(jìn)行裁剪,這時(shí)會觸發(fā)離屏渲染。

6、添加了投影的 layer ,layer.shadow*
7、繪制了文字的 layer ,UILabel, CATextLayer, Core Text 等

設(shè)置圓角+裁剪(cornerRadius+masksToBounds)、透明度+組透明(allowsGroupOpacity+opacity)、陰影等,都是類似的效果,設(shè)置后會應(yīng)用到所有的 subLayer 上,所以 subLayer 處理后,不能立刻丟棄,等待所有 subLayer 處理完成,然后疊加合成,其中就要被保存在 Offscreen buffer 中,這也就觸發(fā)了離屏渲染。

需要注意的是,重寫 drawRect: 方法并不會觸發(fā)離屏渲染。重寫 drawRect: GPU 會等待 CPU 數(shù)據(jù)計(jì)算完成,然后進(jìn)行 GPU 中的渲染操作,并且需要額外開辟內(nèi)存空間。這和標(biāo)準(zhǔn)意義上的離屏渲染并不一樣,在 Instrument 中開啟 Color offscreen rendered yellow 調(diào)試時(shí)也會發(fā)現(xiàn)這并不會被判斷為離屏渲染。

性能優(yōu)化

通過上面的內(nèi)容,大致了解了圖像顯示的過程,屏幕上顯示的內(nèi)容,CUP 計(jì)算好內(nèi)容后交給 GPU 進(jìn)行變換、合成、渲染。隨后 GPU 會把渲染結(jié)果提交到幀緩沖區(qū)去,等待下一次刷新顯示到屏幕上。屏幕 60Hz 的刷新率,每秒顯示 60 幀畫面,CPU 或者 GPU 沒有完成內(nèi)容提交,則那一幀就會被丟棄,等待下一次機(jī)會再顯示,而這時(shí)顯示屏?xí)A糁暗膬?nèi)容不變,這就導(dǎo)致每秒沒有顯示 60 幀畫面,產(chǎn)生了卡頓。
在開發(fā)中保持 APP 的流暢使我們追求的目標(biāo),可以通過 Instuments 工具,查看顯示相關(guān)的數(shù)據(jù),從而定位問題,優(yōu)化性能,提升流暢度。

CUP 資源性能優(yōu)化

  • 對象創(chuàng)建
    對象的創(chuàng)建會分配內(nèi)存、調(diào)整屬性、甚至還有讀取文件等操作,比較消耗 CPU 資源。盡量用輕量的對象代替重量的對象,可以對性能有所優(yōu)化。比如 CALayer 比 UIView 要輕量許多,那么不需要響應(yīng)觸摸事件的控件,用 CALayer 顯示會更加合適。
    盡量推遲對象創(chuàng)建的時(shí)間,并把對象的創(chuàng)建分散到多個任務(wù)中去。如果對象可以復(fù)用,并且復(fù)用的代價(jià)比釋放、創(chuàng)建新對象要小,那么這類對象應(yīng)當(dāng)盡量放到一個緩存池里復(fù)用。

  • 布局計(jì)算
    視圖布局的計(jì)算是 App 中最為常見的消耗 CPU 資源的地方。盡量在后臺線程提前計(jì)算好視圖布局、并且對視圖布局進(jìn)行緩存。
    UIView 的 frame、bounds、transform 等屬性,實(shí)際上都是 CALayer 屬性映射來的,CALayer 內(nèi)部并沒有屬性,當(dāng)調(diào)用屬性方法時(shí),它內(nèi)部是通過運(yùn)行時(shí) resolveInstanceMethod 為對象臨時(shí)添加一個方法,并把對應(yīng)屬性值保存到內(nèi)部的一個 Dictionary 里,同時(shí)還會通知 delegate、創(chuàng)建動畫等等,非常消耗資源。所以對 UIView 的這些屬性進(jìn)行調(diào)整時(shí),消耗的資源要遠(yuǎn)大于一般的屬性,應(yīng)該盡量減少不必要的屬性修改。
    當(dāng)視圖層次調(diào)整時(shí),UIView、CALayer 之間會出現(xiàn)很多方法調(diào)用與通知,所以應(yīng)該盡量避免調(diào)整視圖層次、添加和移除視圖。

  • 文本計(jì)算
    如果一個界面中包含大量文本,計(jì)算文本的寬高,不可避免的會占用很大一部分資源。可以使用 [NSAttributedString boundingRectWithSize:options:context:] 來計(jì)算文本寬高,用 -[NSAttributedString drawWithRect:options:context:] 來繪制文本。盡管這兩個方法性能不錯,但仍舊需要放到后臺線程進(jìn)行以避免阻塞主線程。
    屏幕上能看到的所有文本內(nèi)容控件,在底層都是通過 CoreText 排版、繪制為 Bitmap 顯示的。常見的文本控件 UILabel、UITextView 等,其排版和繪制都是在主線程進(jìn)行的,當(dāng)顯示大量文本時(shí),CPU 的壓力會非常大。可自定義文本控件,用 TextKit 或最底層的 CoreText 對文本異步繪制,可參考第三方文本框架 YYText。CoreText 對象創(chuàng)建好后,能直接獲取文本的寬高等信息,避免了多次計(jì)算,CoreText 對象占用內(nèi)存較少,可以緩存下來以備稍后多次渲染。

  • 圖片的解碼
    盡量使用 PNG 格式圖片,Xcode有對PNG圖片進(jìn)行特殊的算法優(yōu)化。避免使用奇怪的圖片格式, 避免格式轉(zhuǎn)換和調(diào)整圖片大小。一個圖片如果不被GPU支持,那么需要CPU來轉(zhuǎn)換。
    當(dāng)你用 UIImage 或 CGImageSource 的那幾個方法創(chuàng)建圖片時(shí),圖片數(shù)據(jù)并不會立刻解碼。圖片設(shè)置到 UIImageView 或者 CALayer.contents 中去,并且 CALayer 被提交到 GPU 前,CGImage 中的數(shù)據(jù)才會得到解碼。這一步是發(fā)生在主線程的。如果想要繞開這個機(jī)制,常見的做法是在后臺線程先把圖片繪制到 CGBitmapContext 中,然后從 Bitmap 直接創(chuàng)建圖片。

  • 圖形的繪制
    重寫了drawRect 繪制圖像會導(dǎo)致CPU渲染;在CPU進(jìn)行渲染時(shí),GPU大多數(shù)情況是處于等待狀態(tài)。
    由于 CoreGraphic 方法通常都是線程安全的,所以圖像的繪制可以放到后臺線程進(jìn)行。

GUP 資源性能優(yōu)化

相對于 CPU 來說,GPU 能干的事情比較單一:接收提交的紋理(Texture)和頂點(diǎn)描述(三角形),應(yīng)用變換(transform)、混合并渲染,然后輸出到屏幕上。

  • 紋理的渲染
    所有的 Bitmap,包括圖片、文本、柵格化的內(nèi)容,最終都要由內(nèi)存提交到顯存,綁定為 GPU Texture。不論是提交到顯存的過程,還是 GPU 調(diào)整和渲染 Texture 的過程,都要消耗不少 GPU 資源。當(dāng)在較短時(shí)間顯示大量圖片時(shí),CPU 占用率很低,GPU 占用非常高,界面可能會掉幀。避免這種情況的方法只能是盡量減少在短時(shí)間內(nèi)大量圖片的顯示,盡可能將多張圖片合成為一張進(jìn)行顯示。
    當(dāng)圖片過大,超過 GPU 的最大紋理尺寸時(shí),圖片需要先由 CPU 進(jìn)行預(yù)處理,這對 CPU 和 GPU 都會帶來額外的資源消耗。
  • 視圖的混合 (Composing)
    當(dāng)多個視圖(或者說 CALayer)重疊在一起顯示時(shí),GPU 會首先把他們混合到一起。如果視圖結(jié)構(gòu)過于復(fù)雜,混合的過程也會消耗很多 GPU 資源。為了減輕這種情況的 GPU 消耗,應(yīng)用應(yīng)當(dāng)盡量減少視圖數(shù)量和層次,并在不透明的視圖里標(biāo)明 opaque 屬性以避免無用的 Alpha 通道合成。當(dāng)然,這也可以用上面的方法,把多個視圖預(yù)先渲染為一張圖片來顯示。

  • 圖形的生成
    CALayer 的 border、圓角、陰影、遮罩(mask),CASharpLayer 的矢量圖形顯示,通常會觸發(fā)離屏渲染(offscreen rendering),而離屏渲染通常發(fā)生在 GPU 中。當(dāng)一個列表視圖中出現(xiàn)大量圓角的 CALayer,并且快速滑動時(shí),可以觀察到 GPU 資源已經(jīng)占滿,而 CPU 資源消耗很少。這時(shí)界面仍然能正常滑動,但平均幀數(shù)會降到很低。為了避免這種情況,可以嘗試開啟 CALayer.shouldRasterize 光柵化屬性,但這會把原本離屏渲染的操作轉(zhuǎn)嫁到 CPU 上去。
    對于只需要圓角的情況,也可以用一張已經(jīng)繪制好的圓角圖片覆蓋到原本視圖上面來模擬相同的視覺效果。最徹底的解決辦法,就是把需要顯示的圖形在后臺線程繪制為圖片,避免使用圓角、陰影、遮罩等屬性。

References

iOS開發(fā)-視圖渲染與性能優(yōu)化
iOS Rendering 渲染全解析
iOS 圖像渲染原理
iOS 保持界面流暢的技巧
iOS 淺談GPU及App渲染流程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,908評論 2 372