UIView 視圖繪制解析

變更記錄

序號(hào) | 錄入時(shí)間 | 備注
--- | --- | --- | ---
1 | 2018-04-14 | 新建文章
2 | 2018-05-28 | 整理目錄,完善標(biāo)題

UIView的setNeedsLayout, layoutIfNeededlayoutSubviews 方法之間的關(guān)系解釋

iOS layout機(jī)制相關(guān)方法

- (CGSize)sizeThatFits:(CGSize)size
- (void)sizeToFit

- (void)layoutSubviews
- (void)layoutIfNeeded
- (void)setNeedsLayout

- (void)setNeedsDisplay
- (void)drawRect

layoutSubviews在以下情況下會(huì)被調(diào)用:

  1. init初始化不會(huì)觸發(fā)layoutSubviews
    但是是用initWithFrame 進(jìn)行初始化時(shí),當(dāng)rect的值不為CGRectZero時(shí),也會(huì)觸發(fā)——就是改變了frame

  2. addSubview會(huì)觸發(fā)layoutSubviews

  3. 設(shè)置view的Frame會(huì)觸發(fā)layoutSubviews,當(dāng)然前提是frame的值設(shè)置前后發(fā)生了變化

  4. 滾動(dòng)一個(gè)UIScrollView會(huì)觸發(fā)layoutSubviews

  5. 旋轉(zhuǎn)Screen會(huì)觸發(fā)父UIView上的layoutSubviews事件

  6. 改變一個(gè)UIView大小的時(shí)候也會(huì)觸發(fā)父UIView上的layoutSubviews事件

init does not cause layoutSubviews to be called (duh)
addSubview: causes layoutSubviews to be called on the view being added, the view it’s being added to (target view), and all the subviews of the target
view setFrame intelligently calls layoutSubviews on the view having its frame set only if the size parameter of the frame is different
scrolling a UIScrollView causes layoutSubviews to be called on the scrollView, and its superview
rotating a device only calls layoutSubview on the parent view (the responding viewControllers primary view)
Resizing a view will call layoutSubviews on its superview

在蘋果的官方文檔中強(qiáng)調(diào):

      You should override this method only if the autoresizing behaviors of the subviews do not offer the behavior you want.

layoutSubviews, 當(dāng)我們?cè)谀硞€(gè)類的內(nèi)部調(diào)整子視圖位置時(shí),需要調(diào)用。

反過(guò)來(lái)的意思就是說(shuō):如果你想要在外部設(shè)置subviews的位置,就不要重寫。

刷新子對(duì)象布局

  • layoutSubviews方法:這個(gè)方法,默認(rèn)沒(méi)有做任何事情,需要子類進(jìn)行重寫
  • setNeedsLayout方法: 標(biāo)記為需要重新布局,異步調(diào)用layoutIfNeeded刷新布局,不立即刷新,但layoutSubviews一定會(huì)被調(diào)用
  • layoutIfNeeded方法:如果,有需要刷新的標(biāo)記,立即調(diào)用layoutSubviews進(jìn)行布局(如果沒(méi)有標(biāo)記,不會(huì)調(diào)用layoutSubviews)

如果要立即刷新,要先調(diào)用[view setNeedsLayout],把標(biāo)記設(shè)為需要布局,然后馬上調(diào)用[view layoutIfNeeded],實(shí)現(xiàn)布局

在視圖第一次顯示之前,標(biāo)記總是“需要刷新”的,可以直接調(diào)用[view layoutIfNeeded].

重繪

  • drawRect:(CGRect)rect方法:重寫此方法,執(zhí)行重繪任務(wù)
  • setNeedsDisplay方法:標(biāo)記為需要重繪,異步調(diào)用drawRect
  • setNeedsDisplayInRect:(CGRect)invalidRect方法:標(biāo)記為需要局部重繪

sizeToFit會(huì)自動(dòng)調(diào)用sizeThatFits方法;

sizeToFit不應(yīng)該在子類中被重寫,應(yīng)該重寫sizeThatFits

sizeThatFits傳入的參數(shù)是receiver當(dāng)前的size,返回一個(gè)適合的size

sizeToFit可以被手動(dòng)直接調(diào)用

sizeToFitsizeThatFits方法都沒(méi)有遞歸,對(duì)subviews也不負(fù)責(zé),只負(fù)責(zé)自己

———————————-

layoutSubviews對(duì)subviews重新布局

layoutSubviews方法調(diào)用先于drawRect

setNeedsLayout在receiver標(biāo)上一個(gè)需要被重新布局的標(biāo)記,在系統(tǒng)runloop的下一個(gè)周期自動(dòng)調(diào)用layoutSubviews

layoutIfNeeded方法如其名,UIKit會(huì)判斷該receiver是否需要layout.根據(jù)Apple官方文檔,layoutIfNeeded方法應(yīng)該是這樣的

layoutIfNeeded遍歷的不是superview鏈,應(yīng)該是subviews鏈

drawRect是對(duì)receiver的重繪,能獲得context

setNeedDisplay在receiver標(biāo)上一個(gè)需要被重新繪圖的標(biāo)記,在下一個(gè)draw周期自動(dòng)重繪,iphone device的刷新頻率是60hz,也就是1/60秒后重繪


最近在學(xué)習(xí)swift做動(dòng)畫,用到constraint的動(dòng)畫,用到layoutIfNeeded就去研究了下UIView的這幾個(gè)布局的方法。

下面是做得一個(gè)動(dòng)畫,下載地址:AnimationDemo3

下面列舉下iOS layout的相關(guān)方法:

  • layoutSubviews
  • layoutIfNeeded
  • setNeedsLayout
  • setNeedsDisplay
  • drawRect
  • sizeThatFits
  • sizeToFit
    大概常用的上面幾個(gè) , 具體的應(yīng)該還有別的。

layoutSubviews

這個(gè)方法,默認(rèn)沒(méi)有做任何事情,需要子類進(jìn)行重寫 。 系統(tǒng)在很多時(shí)候會(huì)去調(diào)用這個(gè)方法:

  1. 初始化不會(huì)觸發(fā)layoutSubviews,但是如果設(shè)置了不為CGRectZero的frame的時(shí)候就會(huì)觸發(fā)。
  2. addSubview會(huì)觸發(fā)layoutSubviews
  3. 設(shè)置view的Frame會(huì)觸發(fā)layoutSubviews,當(dāng)然前提是frame的值設(shè)置前后發(fā)生了變化
  4. 滾動(dòng)一個(gè)UIScrollView會(huì)觸發(fā)layoutSubviews
  5. 旋轉(zhuǎn)Screen會(huì)觸發(fā)父UIView上的layoutSubviews事件
  6. 改變一個(gè)UIView大小的時(shí)候也會(huì)觸發(fā)父UIView上的layoutSubviews事件

在蘋果的官方文檔中強(qiáng)調(diào): You should override this method only if the autoresizing behaviors of the subviews do not offer the behavior you want.layoutSubviews, 當(dāng)我們?cè)谀硞€(gè)類的內(nèi)部調(diào)整子視圖位置時(shí),需要調(diào)用。反過(guò)來(lái)的意思就是說(shuō):如果你想要在外部設(shè)置subviews的位置,就不要重寫。

setNeedsLayout

標(biāo)記為需要重新布局,不立即刷新,但layoutSubviews一定會(huì)被調(diào)用,配合layoutIfNeeded立即更新

layoutIfNeeded

如果,有需要刷新的標(biāo)記,立即調(diào)用layoutSubviews進(jìn)行布局

這個(gè)動(dòng)畫中有用到 舉個(gè)栗子。

如圖 , 上面有個(gè)label ,中間有個(gè)按鈕 , label已經(jīng)被自動(dòng)布局到左上角 。 然后我們那個(gè)left的constraint
@IBOutlet weak var leftContrain:NSLayoutConstraint!
在viewDidLoad中聲明好,然后在Main.storyboard中進(jìn)行連線。點(diǎn)擊按鈕的時(shí)候 ,我們把左邊的距離改成100 。

在按鈕的點(diǎn)擊事件里加上這句。

leftContrain.constant = 100

然后我們想要一個(gè)動(dòng)畫的效果。
如果這么做

UIView.animateWithDuration(0.8, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: UIViewAnimationOptions.AllowAnimatedContent, animations: {
                self.leftContrain.constant = 100
            }, completion: nil)

你會(huì)發(fā)現(xiàn)然并卵 。其實(shí)這句話self.leftContrain.constant = 100只是執(zhí)行了setNeedsLayout 標(biāo)記了需要重新布局,但是沒(méi)有立即執(zhí)行。所以我們需要在動(dòng)畫中調(diào)用這個(gè)方法layoutIfNeeded
所以代碼應(yīng)該這么寫

leftContrain.constant = 100
UIView.animateWithDuration(0.8, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: UIViewAnimationOptions.AllowAnimatedContent, animations: {
                self.view.layoutIfNeeded() //立即實(shí)現(xiàn)布局
            }, completion: nil)

所以上面不管寫多少約束的改變,只需要在動(dòng)畫里動(dòng)用 一次self.view.layoutIfNeeded(),所有的都會(huì)已動(dòng)畫的方式 。如果一些變化不想動(dòng)畫 。在動(dòng)畫前執(zhí)行self.view.layoutIfNeeded()

drawRect

這個(gè)方法是用來(lái)重繪的。

drawRect在以下情況下會(huì)被調(diào)用:

  1. 如果在UIView初始化時(shí)沒(méi)有設(shè)置rect大小,將直接導(dǎo)致drawRect不被自動(dòng)調(diào)用。drawRect調(diào)用是在Controller->loadView, Controller->viewDidLoad 兩方法之后掉用的.所以不用擔(dān)心在控制器中,這些View的drawRect就開(kāi)始畫了.這樣可以在控制器中設(shè)置一些值給View(如果這些View draw的時(shí)候需要用到某些變量值).
  2. 該方法在調(diào)用sizeToFit后被調(diào)用,所以可以先調(diào)用sizeToFit計(jì)算出size。然后系統(tǒng)自動(dòng)調(diào)用drawRect:方法。
  3. 通過(guò)設(shè)置contentMode屬性值為UIViewContentModeRedraw。那么將在每次設(shè)置或更改frame的時(shí)候自動(dòng)調(diào)用drawRect:
  4. 直接調(diào)用setNeedsDisplay,或者setNeedsDisplayInRect:觸發(fā)drawRect:,但是有個(gè)前提條件是rect不能為0。以上1,2推薦;而3,4不提倡

drawRect方法使用注意點(diǎn):

  1. 若使用UIView繪圖,只能在drawRect:方法中獲取相應(yīng)的contextRef并繪圖。如果在其他方法中獲取將獲取到一個(gè)invalidate的ref并且不能用于畫圖。drawRect:方法不能手動(dòng)顯示調(diào)用,必須通過(guò)調(diào)用setNeedsDisplay 或者 setNeedsDisplayInRect,讓系統(tǒng)自動(dòng)調(diào)該方法。
  2. 若使用CALayer繪圖,只能在drawInContext: 中(類似于drawRect)繪制,或者在delegate中的相應(yīng)方法繪制。同樣也是調(diào)用setNeedDisplay等間接調(diào)用以上方法
  3. 若要實(shí)時(shí)畫圖,不能使用gestureRecognizer,只能使用touchbegan等方法來(lái)掉用setNeedsDisplay實(shí)時(shí)刷新屏幕

sizeToFit

  • sizeToFit會(huì)自動(dòng)調(diào)用sizeThatFits方法;
  • sizeToFit不應(yīng)該在子類中被重寫,應(yīng)該重寫sizeThatFits
  • sizeThatFits傳入的參數(shù)是receiver當(dāng)前的size,返回一個(gè)適合的size
  • sizeToFit可以被手動(dòng)直接調(diào)用sizeToFit和sizeThatFits方法都沒(méi)有遞歸,對(duì)subviews也不負(fù)責(zé),只負(fù)責(zé)自己
    推薦拓展閱讀

ConvertRect

fromView
  CGRect newRect = [self.view convertRect:self.blueView.frame fromView:self.redView];
這段代碼的意思算出在紅色控件里的藍(lán)色控件在控制器view中的位置(其實(shí)就是算x和y的值,因?yàn)閷捀卟蛔儯?toView

  CGRect newRect = [self.blueView convertRect:CGRectMake(50, 50, 100, 100) toView:self.greenView];

調(diào)用視圖 convertRect: 調(diào)用視圖相對(duì)于目標(biāo)視圖的frame toview目標(biāo)視圖

目標(biāo)視圖為nil的時(shí)候指的是Window本身。

Runloop與UIView的繪制

也許要先從Runloop開(kāi)始說(shuō),iOS的mainRunloop是一個(gè)60fps的回調(diào),也就是說(shuō)每16.7ms會(huì)繪制一次屏幕,這個(gè)時(shí)間段內(nèi)要完成view的緩沖區(qū)創(chuàng)建,view內(nèi)容的繪制(如果重寫了drawRect),這些CPU的工作。然后將這個(gè)緩沖區(qū)交給GPU渲染,這個(gè)過(guò)程又包括多個(gè)view的拼接(compositing),紋理的渲染(Texture)等,最終顯示在屏幕上。因此,如果在16.7ms內(nèi)完不成這些操作,比如,CPU做了太多的工作,或者view層次過(guò)于多,圖片過(guò)于大,導(dǎo)致GPU壓力太大,就會(huì)導(dǎo)致“卡”的現(xiàn)象,也就是丟幀。

蘋果官方給出的最佳幀率是:60fps,也就是1幀不丟,當(dāng)然這是理想中的絕佳的體驗(yàn)。

這個(gè)60fps改怎么理解呢?一般來(lái)說(shuō)如果幀率達(dá)到25+fps,人眼就基本感覺(jué)不到停頓了,因此,如果你能讓你ios程序穩(wěn)定的保持在30fps已經(jīng)很不錯(cuò)了,注意,是“穩(wěn)定”在30fps,而不是,10fps,40fps,20fps這樣的跳動(dòng),如果幀頻不穩(wěn)就會(huì)有卡的感覺(jué)。60fps真的很難達(dá)到,尤其在iphone4,4s上。

總的來(lái)說(shuō),UIView從繪制到Render的過(guò)程有如下幾步:

每一個(gè)UIView都有一個(gè)layer,每一個(gè)layer都有個(gè)content,這個(gè)content指向的是一塊緩存,叫做backing store。

UIView的繪制和渲染是兩個(gè)過(guò)程,當(dāng)UIView被繪制時(shí),CPU執(zhí)行drawRect,通過(guò)context將數(shù)據(jù)寫入backing store

當(dāng)backing store寫完后,通過(guò)render server交給GPU去渲染,將backing store中的bitmap數(shù)據(jù)顯示在屏幕上

上面提到的從CPU到GPU的過(guò)程可用下圖表示:

image

下面具體來(lái)討論下這個(gè)過(guò)程

CPU bound:

假設(shè)我們創(chuàng)建一個(gè)UILabel:

UILabel* label = [[UILabel alloc]initWithFrame:CGRectMake(10, 50, 300, 14)];
label.backgroundColor = [UIColor whiteColor];
label.font = [UIFont systemFontOfSize:14.0f];
label.text = @"test";
[self.view addSubview:label];

這個(gè)時(shí)候不會(huì)發(fā)生任何操作,由于UILabel重寫了drawRect,因此,這個(gè)view會(huì)被marked as “dirty”:

類似這個(gè)樣子:


image

然后一個(gè)新的Runloop到來(lái),上面說(shuō)道在這個(gè)Runloop中需要將界面渲染上去,對(duì)于UIKit的渲染,Apple用的是它的Core Animation。

做法是在Runloop開(kāi)始的時(shí)候調(diào)用:

[CATransaction begin]

在Runloop結(jié)束的時(shí)候調(diào)用

[CATransaction commit]

在begin和commit之間做的事情是將view增加到view hierarchy中,這個(gè)時(shí)候也不會(huì)發(fā)生任何繪制的操作。

當(dāng)[CATransaction commit]執(zhí)行完后,CPU開(kāi)始繪制這個(gè)view:

首先CPU會(huì)為layer分配一塊內(nèi)存用來(lái)繪制bitmap,叫做backing store

創(chuàng)建指向這塊bitmap緩沖區(qū)的指針,叫做CGContextRef

通過(guò)Core Graphic的api,也叫Quartz2D,繪制bitmap

將layer的content指向生成的bitmap

清空dirty flag標(biāo)記

這樣CPU的繪制基本上就完成了。

通過(guò)time profiler 可以完整的看到個(gè)過(guò)程:

Running Time Self Symbol Name
2.0ms 1.2% 0.0 +[CATransaction flush]
2.0ms 1.2% 0.0 CA::Transaction::commit()
2.0ms 1.2% 0.0 CA::Context::commit_transaction(CA::Transaction*)
1.0ms 0.6% 0.0 CA::Layer::layout_and_display_if_needed(CA::Transaction*)
1.0ms 0.6% 0.0 CA::Layer::display_if_needed(CA::Transaction*)
1.0ms 0.6% 0.0 -[CALayer display]
1.0ms 0.6% 0.0 CA::Layer::display()
1.0ms 0.6% 0.0 -[CALayer _display]
1.0ms 0.6% 0.0 CA::Layer::display_()
1.0ms 0.6% 0.0 CABackingStoreUpdate_
1.0ms 0.6% 0.0 backing_callback(CGContext*, void*)
1.0ms 0.6% 0.0 -[CALayer drawInContext:]
1.0ms 0.6% 0.0 -[UIView(CALayerDelegate) drawLayer:inContext:]
1.0ms 0.6% 0.0 -[UILabel drawRect:]
1.0ms 0.6% 0.0 -[UILabel drawTextInRect:]

假如某個(gè)時(shí)刻修改了label的text:

    label.text = @"hello world";

由于內(nèi)容變了,layer的content的bitmap的尺寸也要變化,因此這個(gè)時(shí)候當(dāng)新的Runloop到來(lái)時(shí),CPU要為layer重新創(chuàng)建一個(gè)backing store,重新繪制bitmap。

CPU這一塊最耗時(shí)的地方往往在Core Graphic的繪制上,關(guān)于Core Graphic的性能優(yōu)化是另一個(gè)話題了,又會(huì)牽扯到很多東西,就不在這里討論了。

GPU bound:

CPU完成了它的任務(wù):將view變成了bitmap,然后就是GPU的工作了,GPU處理的單位是Texture。

基本上我們控制GPU都是通過(guò)OpenGL來(lái)完成的,但是從bitmap到Texture之間需要一座橋梁,Core Animation正好充當(dāng)了這個(gè)角色:

Core Animation對(duì)OpenGL的api有一層封裝,當(dāng)我們的要渲染的layer已經(jīng)有了bitmap content的時(shí)候,這個(gè)content一般來(lái)說(shuō)是一個(gè)CGImageRef,CoreAnimation會(huì)創(chuàng)建一個(gè)OpenGL的Texture并將CGImageRef(bitmap)和這個(gè)Texture綁定,通過(guò)TextureID來(lái)標(biāo)識(shí)。

這個(gè)對(duì)應(yīng)關(guān)系建立起來(lái)之后,剩下的任務(wù)就是GPU如何將Texture渲染到屏幕上了。

GPU大致的工作模式如下:


image

整個(gè)過(guò)程也就是一件事:CPU將準(zhǔn)備好的bitmap放到RAM里,GPU去搬這快內(nèi)存到VRAM中處理。

而這個(gè)過(guò)程GPU所能承受的極限大概在16.7ms完成一幀的處理,所以最開(kāi)始提到的60fps其實(shí)就是GPU能處理的最高頻率。

因此,GPU的挑戰(zhàn)有兩個(gè):

將數(shù)據(jù)從RAM搬到VRAM中

將Texture渲染到屏幕上

這兩個(gè)中瓶頸基本在第二點(diǎn)上。渲染Texture基本要處理這么幾個(gè)問(wèn)題:

Compositing:

Compositing是指將多個(gè)紋理拼到一起的過(guò)程,對(duì)應(yīng)UIKit,是指處理多個(gè)view合到一起的情況,如

[self.view addsubview : subview]。
如果view之間沒(méi)有疊加,那么GPU只需要做普通渲染即可。 如果多個(gè)view之間有疊加部分,GPU需要做blending。

加入兩個(gè)view大小相同,一個(gè)疊加在另一個(gè)上面,那么計(jì)算公式如下:

R = S+D*(1-Sa)

R: 為最終的像素值

S: 代表 上面的Texture(Top Texture)

D: 代表下面的Texture(lower Texture)

其中S,D都已經(jīng)pre-multiplied各自的alpha值。

Sa代表Texture的alpha值。

假如Top Texture(上層view)的alpha值為1,即不透明。那么它會(huì)遮住下層的Texture。即,R = S。是合理的。 假如Top Texture(上層view)的alpha值為0.5,S 為 (1,0,0),乘以alpha后為(0.5,0,0)。D為(0,0,1)。 得到的R為(0.5,0,0.5)。

基本上每個(gè)像素點(diǎn)都需要這么計(jì)算一次。

因此,view的層級(jí)很復(fù)雜,或者view都是半透明的(alpha值不為1)都會(huì)帶來(lái)GPU額外的計(jì)算工作。

Size

這個(gè)問(wèn)題,主要是處理image帶來(lái)的,假如內(nèi)存里有一張400x400的圖片,要放到100x100的imageview里,如果不做任何處理,直接丟進(jìn)去,問(wèn)題就大了,這意味著,GPU需要對(duì)大圖進(jìn)行縮放到小的區(qū)域顯示,需要做像素點(diǎn)的sampling,這種smapling的代價(jià)很高,又需要兼顧pixel alignment。計(jì)算量會(huì)飆升。

Offscreen Rendering And Mask

如果我們對(duì)layer做這樣的操作:

label.layer.cornerRadius = 5.0f;
label.layer.masksToBounds = YES;

會(huì)產(chǎn)生offscreen rendering,它帶來(lái)的最大的問(wèn)題是,當(dāng)渲染這樣的layer的時(shí)候,需要額外開(kāi)辟內(nèi)存,繪制好radius,mask,然后再將繪制好的bitmap重新賦值給layer。

因此繼續(xù)性能的考慮,Quartz提供了優(yōu)化的api:

label.layer.cornerRadius = 5.0f;
label.layer.masksToBounds = YES;
label.layer.shouldRasterize = YES;
label.layer.rasterizationScale = label.layer.contentsScale;

簡(jiǎn)單的說(shuō),這是一種cache機(jī)制。
同樣GPU的性能也可以通過(guò)instrument去衡量:

紅色代表GPU需要做額外的工作來(lái)渲染View,綠色代表GPU無(wú)需做額外的工作來(lái)處理bitmap。

That’s all

layoutSubviews調(diào)用總結(jié)

  1. 自身的frame發(fā)生變化, 會(huì)重新布局layoutSubviews
  2. 添加視圖,調(diào)用addSubView的時(shí)候
  3. 滾動(dòng)一個(gè)UIScrollView會(huì)觸發(fā)
  4. 子視圖frame發(fā)生變化,會(huì)調(diào)用父視圖的addSubView

Its own bounds (not frame) changed.
The bounds of one of its direct subviews changed.
A subview is added to the view or removed from the view.

  • init does not cause layoutSubviews to be called (duh)
  • addSubview causes layoutSubviews to be called on the view being added, the view it’s being added to (target view), and all the subviews of the target view
  • setFrame intelligently calls layoutSubviews on the view having it’s frame set only if the size parameter of the frame is different
  • scrolling a UIScrollView causes layoutSubviews to be called on the scrollView, and it’s superview
  • rotating a device only calls layoutSubview on the parent view (the responding viewControllers primary view)
  • removeFromSuperview – layoutSubviews is called on superview only (not show in table)

參考

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

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