iOS 底層原理39:Instruments系列(三)Animation Hitches

iOS 底層原理 文章匯總

引言

從Xcode12開(kāi)始,Instrument更新了UI,新增了一個(gè)模板 Animation Hitches 用來(lái)分析用戶(hù)的 app 中的卡頓,并去除了 Core Animation 檢測(cè)方式。在 iPhone13Pro 之前 iPhone 屏幕最高刷新頻率仍為 60 HZ,而在支持 PromotionDisplay 的設(shè)備上幀率可調(diào)整至 120 幀,并且會(huì)根據(jù)當(dāng)前用戶(hù)手勢(shì)和設(shè)備狀態(tài)進(jìn)行動(dòng)態(tài)調(diào)整。此時(shí)再繼續(xù)使用幀率來(lái)判斷性能的好壞及流暢度將會(huì)是一個(gè)錯(cuò)誤的選擇。所以 Animation Hitches 主要用于代替幀率檢測(cè),并且提出 卡頓時(shí)間比(Hitch Time Ratio) 的概念用于替代 FPS。

在 Hitch 提出之前,都是借助FPS (Frames Per Second 幀率),即每秒繪制幀的數(shù)量來(lái)衡量頁(yè)面是否卡頓

  • 滑動(dòng)屏幕時(shí),幀率理想值為60FPS
  • 滑動(dòng)屏幕時(shí),幀率越高表示性能越好;幀率過(guò)低意味著屏幕可能出現(xiàn)卡頓,存在隨機(jī)丟幀的可能。
  • 其中幀率>=57為優(yōu)秀; >=55為良好; >=50為可接受;

蘋(píng)果于 20 年的 Session 中提出了 Hitch 的概念,用以衡量滑動(dòng)時(shí)的卡頓情況。Hitch 指的是 卡頓時(shí)間(一幀延后出現(xiàn)的時(shí)間,ms)/ 總時(shí)間(一般是 1 秒),簡(jiǎn)單來(lái)說(shuō) 卡頓時(shí)間比就是一個(gè)區(qū)間內(nèi)的總卡頓時(shí)間除以它的持續(xù)時(shí)間。

  • 低于 5 ms/s 說(shuō)明比較優(yōu)秀,是最不易被用戶(hù)察覺(jué)到的
  • 介于 5ms/s 和 10ms/s 之間,說(shuō)明發(fā)生了中等卡頓,用戶(hù)會(huì)察覺(jué)到一些中斷,但并不嚴(yán)重
  • 高于 10 ms/s 說(shuō)明發(fā)生了較嚴(yán)重的卡頓,已經(jīng)影響了用戶(hù)體驗(yàn)。
    Hitch

卡頓

  • 概念:任何時(shí)候屏幕上出現(xiàn)晚于預(yù)計(jì)的幀都屬于卡頓
  • 簡(jiǎn)單來(lái)說(shuō)就是掉幀了,即沒(méi)有在規(guī)定時(shí)間內(nèi)渲染好一幀畫(huà)面,這就是卡頓一次


    卡頓

如上圖所示,當(dāng)手指在屏幕上滑動(dòng)時(shí),滾動(dòng)視圖會(huì)隨著手勢(shì)做出響應(yīng),如果一幀一幀來(lái)看,就是每一幀都對(duì)應(yīng)手指位置的變化。當(dāng)卡頓發(fā)生時(shí),某一幀沒(méi)有跟隨手指變化,導(dǎo)致到下一幀時(shí),產(chǎn)生跳躍,打破了用戶(hù)和屏幕內(nèi)容的視覺(jué)連接感。圖中卡頓產(chǎn)生的原因就是第三幀重復(fù)了,主要是因?yàn)榈谒膸难舆t導(dǎo)致了第三幀占用了兩幀的時(shí)間,給用戶(hù)看到的就是卡頓掉幀的現(xiàn)象。

RenderLoop

  • 概念:是一個(gè)連續(xù)的過(guò)程,通過(guò)用戶(hù)手勢(shì)等將事件傳給 App,接著 App 向操作系統(tǒng)傳遞事件并最終響應(yīng)事件,再將響應(yīng)傳遞給用戶(hù)的過(guò)程


    RenderLoop

    RenderLoop 的時(shí)間隨著設(shè)備刷新頻率,在 iPhone13 Pro(Max) 以下的 iPhone 設(shè)備最大均為 60 幀,而 iPhone13 Pro(Max) 及 iPadPro 則最高支持 120 幀,也就是最短僅需每 8.33 毫秒就可以顯示一個(gè)新幀。


    RenderLoop刷新頻率

視圖渲染流程

在每一幀顯示的過(guò)程中,大概可以分為3個(gè)階段,如下所示


渲染流程
  • App:進(jìn)行用戶(hù)事件處理
  • Render server:負(fù)責(zé)將圖層樹(shù)轉(zhuǎn)換為可顯示的圖像(即用戶(hù)界面繪制)
  • On the display:顯示緩存的幀


1、AppRender server 階段需要在下一個(gè) VSYNC 到來(lái)之前完成
2、這里運(yùn)用了 雙緩存區(qū) + 垂直同步機(jī)制,主要是用于解決 屏幕撕裂現(xiàn)象
3、整個(gè)渲染階段可以分為5個(gè)階段:

渲染階段細(xì)分

  • 階段 1 + 2:App(Event + Commit)
  • 階段 3 + 4:Render server(Prepare + Excute)
  • 階段 5:Display

4、整體渲染流程如下

  • Event(事件階段)通過(guò) touch、timer 等事件決定用戶(hù)界面是否需要改變
  • Commit(提交階段),App會(huì)向 Render server(渲染服務(wù)器)提交渲染命令
  • Prepare (準(zhǔn)備階段)會(huì)為 GPU 的繪制組好準(zhǔn)備
  • Excute(執(zhí)行階段)會(huì)由 GPU 將用戶(hù)界面的圖像繪制出來(lái)
  • Display(顯示階段)會(huì)將緩沖區(qū)的幀交換到屏幕上顯示

下面以一個(gè)帶有陰影的渲染圖形為例,通過(guò)觀(guān)察 RenderLoop 中每一幀所做的工作,來(lái)分別介紹不同階段

App 階段

App 階段包含 2 個(gè)階段,分別是 Event 、Commit。其中Commit 又分為 4 個(gè)子階段,分別是

  • Layout
  • Display
  • Prepare
  • Commit

階段 1:Event 事件階段

  • 事件階段 表示 App 接收到了事件(例如:touch、網(wǎng)絡(luò)請(qǐng)求回調(diào)、鍵盤(pán)、timer等)。


    事件階段-Event
  • 在 App 中可以通過(guò)改變其層級(jí)結(jié)構(gòu),或者使用其他方式響應(yīng)事件。例如圖層顏色/大小/位置變化。當(dāng) App 更新了圖層時(shí), CoreAnimation 會(huì)同時(shí)調(diào)用 setNeedsLayout 方法,該方法能夠找出哪些圖層需要重新計(jì)算布局,且系統(tǒng)會(huì)合并這些重新計(jì)算的請(qǐng)求,并在 Commit 階段按需執(zhí)行,以此來(lái)減少重復(fù)工作

    層級(jí)變化

階段 2: Commit 提交階段

提交階段還可以細(xì)分,主要分為4個(gè)子階段


提交階段-Commit
  • layout (布局階段):layoutSubviews 會(huì)被所有需要布局的 View 調(diào)用
  • display(顯示階段):drawRect 會(huì)被每個(gè)需要被更新的 View 調(diào)用
  • prpare(準(zhǔn)備階段):未解碼圖像進(jìn)一步解碼(即需要優(yōu)化的常見(jiàn)的圖片主線(xiàn)程解碼操作)
  • commit(提交階段):視圖樹(shù)將會(huì)被遞歸打包并發(fā)送到 RenderServer 中
Layout - 布局階段

在布局階段,layoutSubviews 會(huì)被所有需要布局的 View 調(diào)用。例如視圖布局(frame、bounds、tranform等)、增加/移除視圖、直接調(diào)用 setNeedsLayout/layoutIfNeesed 等

注:這些操作并不是立即執(zhí)行,系統(tǒng)會(huì)合并布局請(qǐng)求,在 Runloop 休眠前進(jìn)行統(tǒng)一處理

Display - 顯示階段
  • 在顯示階段,drawRect 會(huì)被每個(gè)需要被更新的 View 調(diào)用,例如 UILabel 等空間類(lèi)或者 任何重寫(xiě) drawRect 方法 的類(lèi),必須調(diào)用 調(diào)用 setNeedsDisplay 用以支持 View 的更新。
  • 非必須不要重寫(xiě) drawRect 方法,因?yàn)樵诶L制時(shí),每個(gè)自定義圖層都會(huì)接收到帶紋理的 CoreGraphics 的背景,會(huì)利用 CoreAnimation 進(jìn)行繪制,這些圖層就變成了圖片
    • 導(dǎo)致內(nèi)存額外的開(kāi)銷(xiāo)以及bitmap的存儲(chǔ),對(duì)整體內(nèi)存壓力較大
    • 由于是在 CPU上進(jìn)行繪制,還增加了整體主線(xiàn)程的占用
drawRect
Prepare - 準(zhǔn)備階段

在準(zhǔn)備階段,主要是將還未解碼的圖像進(jìn)行進(jìn)一步解碼,這也是我們需要優(yōu)化的點(diǎn)(即優(yōu)化圖片主線(xiàn)程解碼操作)。

因?yàn)閷?duì)于每個(gè)解碼的圖像,App可能會(huì)持續(xù)存在大量的內(nèi)存分配(與圖像大小成正比),當(dāng)App占用內(nèi)存越來(lái)越多時(shí),操作系統(tǒng)就會(huì)開(kāi)始?jí)嚎s物理內(nèi)存(physical memory),這整個(gè)過(guò)程都需要CPU參與,所以除了App會(huì)使用CPU,還增加了無(wú)法控制的全局 CPU 使用率,導(dǎo)致App消耗更多的物理內(nèi)存,此時(shí)操作系統(tǒng)會(huì)終止低優(yōu)先級(jí)的后臺(tái)進(jìn)程,從而釋放更多的物理內(nèi)存。但設(shè)備的物理內(nèi)存始終是有限的,當(dāng)App對(duì)內(nèi)存的消耗達(dá)到了臨界值時(shí),該App進(jìn)程就會(huì)被操作系統(tǒng)終止,這就是常說(shuō)的大圖導(dǎo)致的OOM

若某個(gè)圖像的顏色格式 GPU 無(wú)法直接使用,也會(huì)在這一步進(jìn)行格式轉(zhuǎn)換。這就要求對(duì)該圖像進(jìn)行 copy 操作,而不是直接使用指針,這樣會(huì)耗時(shí)更長(zhǎng)及占用更多的內(nèi)存。

Commit - 提交階段

在提交階段,視圖樹(shù)會(huì)被遞歸打包,并發(fā)送到 Render Server中,所以當(dāng)視圖圖層較復(fù)雜時(shí),這個(gè)過(guò)程的耗時(shí)也會(huì)相對(duì)較長(zhǎng),這也是我們經(jīng)常提及的優(yōu)化點(diǎn)(即盡量減輕視圖層級(jí)結(jié)構(gòu),不要跟套娃似的,無(wú)窮無(wú)盡)。

Render Server 階段

Render Server(渲染服務(wù)器)主要負(fù)責(zé)將圖層樹(shù)轉(zhuǎn)換為真正顯示的圖像,分為兩個(gè)子階段


Render Server 階段
  • prepare:圖層樹(shù)被編譯成一系列簡(jiǎn)單的指令,供 GPU 執(zhí)行,幀動(dòng)畫(huà)也在此處進(jìn)行處理
  • excute:GPU 將 App 的圖層繪制成最終圖像

階段 3:Prepare 準(zhǔn)備階段

  • 在 準(zhǔn)備階段,RenderServer 會(huì)廣度優(yōu)先遍歷 App 的圖層樹(shù),準(zhǔn)備一個(gè)線(xiàn)性管線(xiàn),這樣 GPU 就能按照順序執(zhí)行命令進(jìn)行繪制。


    遍歷圖層樹(shù)
  • 從根圖層開(kāi)始逐層遍歷,最終才有了 GPU 可以在下一個(gè)執(zhí)行階段執(zhí)行的整個(gè)管線(xiàn)。


    逐層遍歷

階段 4:Excute 執(zhí)行階段

在執(zhí)行階段,主要是由 GPU 根據(jù)前面 prepare 階段準(zhǔn)備好的圖層樹(shù)進(jìn)行頂點(diǎn)著色、形狀裝配、幾何著色、光柵化、片段著色與圖層混合。一旦 GPU 執(zhí)行完會(huì)將渲染好的圖像放入幀緩存區(qū)中等待下一個(gè) VSYNC 的到來(lái)并交換到屏幕上進(jìn)行顯示。

執(zhí)行階段-Excute

Display 階段

階段 5:Display 顯示階段

在顯示階段,主要是將幀緩存區(qū)中的內(nèi)容交換到顯示器上進(jìn)行最終顯示

視圖渲染流程總結(jié)

  • App:進(jìn)行用戶(hù)事件的處理
    • Event:App接收到事件(touch、網(wǎng)絡(luò)請(qǐng)求、鍵盤(pán)、timer等)
    • Commit
      • layout (布局階段):layoutSubviews 會(huì)被所有需要布局的 View 調(diào)用
      • display(顯示階段):drawRect 會(huì)被每個(gè)需要被更新的 View 調(diào)用
      • prpare(準(zhǔn)備階段):未解碼圖像進(jìn)一步解碼(即需要優(yōu)化的常見(jiàn)的圖片主線(xiàn)程解碼操作)
      • commit(提交階段):視圖樹(shù)將會(huì)被遞歸打包并發(fā)送到 RenderServer 中
  • RenderServer:負(fù)責(zé)將圖層樹(shù)轉(zhuǎn)換為可顯示的圖像(即用戶(hù)界面繪制)
    • prepare:圖層樹(shù)被編譯成一系列簡(jiǎn)單的指令,供 GPU 執(zhí)行,幀動(dòng)畫(huà)也在此處進(jìn)行處理
    • excute:GPU 將 App 的圖層繪制成最終圖像
  • Display:將緩沖的幀顯示出來(lái)

想了解離屏渲染的同學(xué)請(qǐng)閱讀# 屏幕卡頓 及 iOS中的渲染流程解析

卡頓類(lèi)型

通過(guò)了解了視圖渲染的工作流程,其主要工作是在App 和 Render Server 中進(jìn)行的,所以總共涉及兩種卡頓類(lèi)型

  • 提交卡頓(App 階段)
  • 渲染卡頓(Render Server 階段)
    卡頓類(lèi)型

提交卡頓

  • 提交卡頓:是指 App 話(huà)費(fèi)過(guò)長(zhǎng)的時(shí)間來(lái)處理/提交事件
    提交卡頓

    如上圖所示,在提交階段耗時(shí)過(guò)長(zhǎng),從而導(dǎo)致錯(cuò)過(guò)了截止時(shí)間,所以在下一個(gè) VSYNC 中 Render Server 沒(méi)有需要處理的事情,必須要等待下一個(gè) VSYNC 到了后才開(kāi)始渲染。簡(jiǎn)單來(lái)說(shuō)就是把幀傳送的時(shí)間延遲了一幀(即 16.67ms),這個(gè)延遲時(shí)間 即為 卡頓時(shí)間(Hitch time)

如何避免提交卡頓?

主要有以下幾種方式

  • 保持視圖輕量
  • 避免復(fù)雜布局
  • 合理運(yùn)用多線(xiàn)程能力

下面進(jìn)行詳細(xì)說(shuō)明

保持視圖的輕量
  • 盡可能利用 CALayer 上GPU 加速的可用屬性,如非必要避免使用CPU進(jìn)行自定義繪制
  • 非必要情況下,避免重寫(xiě) drawRect 方法,因?yàn)闀?huì)導(dǎo)致額外的內(nèi)存開(kāi)銷(xiāo)。

針對(duì)于文本、圖片等原本就在 CPU 上進(jìn)行繪制的系統(tǒng)控件,我們可以嘗試使用其更底層線(xiàn)程安全的 CoreGraphics 能力,比如 TextKit、CoreText 等搭配多線(xiàn)程異步繪制減輕主線(xiàn)程壓力。

  • 盡量復(fù)用視圖,避免重復(fù)的添加/移除視圖
  • 如果需要將某一個(gè)視圖從某一個(gè)動(dòng)畫(huà)中移除,盡量使用 hidden 屬性
  • 對(duì)于 Prepare 階段,當(dāng)我們的 UIImage 容器視圖的大小小于圖片本身時(shí),我們通常可以使用 下采樣技術(shù)(downsampling) 來(lái)進(jìn)行縮略圖的創(chuàng)建以節(jié)省部分內(nèi)存空間。
避免復(fù)雜布局
  • 減少代價(jià)過(guò)高且重復(fù)的布局,在需要更新布局時(shí)盡量只使用 setNeedsLayout

layoutIfNeeded 會(huì)消耗當(dāng)前事務(wù)的生命周期也會(huì)造成卡頓,大多數(shù)時(shí)候你可以等到下一次 Runloop 執(zhí)行時(shí)再更新你的布局。

  • 避免復(fù)雜布局約束,嘗試使用最少的約束來(lái)完成布局
  • 避免遞歸布局,即視圖應(yīng)該只能使自己或自己的子視圖無(wú)效,而不能使其同級(jí)視圖或父視圖無(wú)效
  • 避免非必要的視圖層級(jí),復(fù)雜的視圖層級(jí)會(huì)增加提交階段的整體耗時(shí)
合理運(yùn)用多線(xiàn)程能力
  • 利用GCD的多線(xiàn)程能力,充分利用 CPU 多核優(yōu)勢(shì),提前在子線(xiàn)程進(jìn)行布局等 UI 無(wú)關(guān)操作,避免主線(xiàn)程掛起(hang)。
  • 避免主線(xiàn)程 IO 等磁盤(pán)相關(guān)操作
  • 針對(duì)于常見(jiàn)的主線(xiàn)程解碼操作,
    • 在 iOS15 之前,我們通常都是自己封裝或是利用最常見(jiàn)的第三方庫(kù) SDWebImage 替我們?cè)谧泳€(xiàn)程進(jìn)行解碼操作。
    • 在 iOS15 中,Apple 終于提供了官方的解決方案以解決該問(wèn)題:UIImage 的 prepareThumbnailOfSize:completionHandler: 等新接口。
  • 針對(duì)于必須在 CPU 上進(jìn)行繪制的組件,嘗試結(jié)合多線(xiàn)程使用異步繪制能力減輕主線(xiàn)程壓力。

渲染卡頓

  • 渲染卡頓:是指 Render Server 無(wú)法按時(shí)準(zhǔn)備/執(zhí)行圖層樹(shù)的出現(xiàn),即 Excute 階段耗時(shí)超過(guò)了 VSYNC 的界限,導(dǎo)致本來(lái)應(yīng)該渲染的幀為準(zhǔn)備好。


    渲染卡頓

    如上圖所示,綠色的畫(huà)面比預(yù)期的晚了一幀于是有了 16 毫秒的卡頓。

如何避免渲染卡頓?

Prepare 階段對(duì)卡頓的影響較少,主要還是在 Excute 階段的離屏渲染。針對(duì)離屏渲染的優(yōu)化,請(qǐng)閱讀
# iOS 常見(jiàn)觸發(fā)離屏渲染場(chǎng)景及優(yōu)化方案總結(jié)

  • 對(duì)于陰影來(lái)說(shuō),在設(shè)置陰影時(shí)確保設(shè)置 shadowPath 以減少大量離屏通道
  • 在圓化矩形時(shí),使用 cornerRadiuscornerCurve 屬性避免用蒙版或角內(nèi)容來(lái)構(gòu)成圓角矩形。
  • 優(yōu)化整個(gè) App 的 Mask。

使用 masksToBounds 遮蔽為矩形圓角矩形或橢圓形的性能比自定義蒙版圖層好得多

  • 合理并謹(jǐn)慎的使用 shouldRasterize 屬性,

它會(huì)對(duì)一塊圖層進(jìn)行光柵化操作并進(jìn)行緩存。若針對(duì)于需要頻繁刷新的圖層使用該屬性反而對(duì)性能有著負(fù)面影響。

  • 盡量使用非透明的圖層
  • 盡量減少圖層混合
  • 重要的是用 Instruments 來(lái)對(duì) App 進(jìn)行分析并檢查圖層樹(shù)以獲得重要的技巧從而降低整體離屏計(jì)數(shù)。

下面就主要介紹 Instrument 中 Animation Hitches 的使用

使用

  • 選中 Instrument 中的 Animation Hitches

    Animation Hitches

  • 啟動(dòng)程序,會(huì)顯示recording 此時(shí)操作界面卡頓的位置工具會(huì)記錄


    記錄
  • 然后再次點(diǎn)擊關(guān)閉等待Analyze分析完成后顯示如下界面,找出耗時(shí)的函數(shù)


    耗時(shí)函數(shù)
  • 最后分析,并根據(jù)實(shí)際情況解決問(wèn)題

參考文章

Tech Talk - Hitches 與 渲染循環(huán)
iOS 性能檢測(cè)新方式——AnimationHitches
Animation Hitches in iOS Development
Explore UI animation hitches and the render loop - Tech Talks - Videos - Apple Developer
Find and fix hitches in the commit phase - Tech Talks - Videos
iOS 性能分析-阿里
iOS 高刷屏監(jiān)控 + 優(yōu)化:從理論到實(shí)踐全面解析 -字節(jié)
WWDC20 10077 - 使用 XCTest 消除動(dòng)畫(huà)卡頓
# APP 性能優(yōu)化終極求生指南
# iOS性能優(yōu)化之界面卡頓監(jiān)測(cè)
# iOS 高刷屏監(jiān)控 + 優(yōu)化:從理論到實(shí)踐全面解析
## 精確定位頁(yè)面滑動(dòng)幀率瓶頸及優(yōu)化參考

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

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

  • 前言 通常程序運(yùn)行時(shí)常見(jiàn)的錯(cuò)誤大致分為兩類(lèi) 用戶(hù)使用引起的外部錯(cuò)誤,例如缺少參數(shù)、參數(shù)錯(cuò)誤等 程序運(yùn)行時(shí)產(chǎn)生的內(nèi)部...
    Style_月月閱讀 1,990評(píng)論 2 12
  • 界面優(yōu)化無(wú)非就是解決卡頓問(wèn),優(yōu)化界面流暢度,以下就通過(guò)先分析卡頓的原因,然后再介紹具體的優(yōu)化方案,來(lái)分析如何做界面...
    大菠蘿_DABLO閱讀 485評(píng)論 0 2
  • 怎樣將oc代碼反編譯成C和C++代碼?使用xcode內(nèi)置的LLVM的前端編譯器clang,這樣生成的代碼并不完全是...
    東也_閱讀 1,982評(píng)論 4 14
  • 寫(xiě)在前面:這篇文章并非原創(chuàng),是對(duì)iOS 保持界面流暢的技巧[https://blog.ibireme.com/20...
    蔚尼閱讀 2,594評(píng)論 0 8
  • Head 在性能優(yōu)化中,有一個(gè)重要的知識(shí)點(diǎn)就是卡頓優(yōu)化,我們以FPS(每秒傳輸幀數(shù)(Frames Per Seco...
    寧夏灼雪__閱讀 2,578評(píng)論 0 7