iOS界面流暢技巧之微博 Demo 性能優化技巧

来源:https://www.cnblogs.com/Julday/archive/2019/11/20/11897660.html
-Advertisement-
Play Games

微博 Demo 性能優化技巧 我為了演示 YYKit 的功能,實現了微博和 Twitter 的 Demo,併為它們做了不少性能優化,下麵就是優化時用到的一些技巧。 預排版 當獲取到 API JSON 數據後,我會把每條 Cell 需要的數據都在後臺線程計算並封裝為一個佈局對象 CellLayout。 ...


微博 Demo 性能優化技巧

我為了演示 YYKit 的功能,實現了微博和 Twitter 的 Demo,併為它們做了不少性能優化,下麵就是優化時用到的一些技巧。

預排版

當獲取到 API JSON 數據後,我會把每條 Cell 需要的數據都在後臺線程計算並封裝為一個佈局對象 CellLayout。CellLayout 包含所有文本的 CoreText 排版結果、Cell 內部每個控制項的高度、Cell 的整體高度。每個 CellLayout 的記憶體占用並不多,所以當生成後,可以全部緩存到記憶體,以供稍後使用。這樣,TableView 在請求各個高度函數時,不會消耗任何多餘計算量;當把 CellLayout 設置到 Cell 內部時,Cell 內部也不用再計算佈局了。

對於通常的 TableView 來說,提前在後臺計算好佈局結果是非常重要的一個性能優化點。為了達到最高性能,你可能需要犧牲一些開發速度,不要用 Autolayout 等技術,少用 UILabel 等文本控制項。但如果你對性能的要求並不那麼高,可以嘗試用 TableView 的預估高度的功能,並把每個 Cell 高度緩存下來。這裡有個來自百度知道團隊的開源項目可以很方便的幫你實現這一點:FDTemplateLayoutCell。

預渲染

微博的頭像在某次改版中換成了圓形,所以我也跟進了一下。當頭像下載下來後,我會在後臺線程將頭像預先渲染為圓形並單獨保存到一個 ImageCache 中去。

一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS交流群:1012951431, 分享BAT,阿裡面試題、面試經驗,討論技術, 大家一起交流學習成長!希望幫助開發者少走彎路。

對於 TableView 來說,Cell 內容的離屏渲染會帶來較大的 GPU 消耗。在 Twitter Demo 中,我為了圖省事兒用到了不少 layer 的圓角屬性,你可以在低性能的設備(比如 iPad 3)上快速滑動一下這個列表,能感受到雖然列表並沒有較大的卡頓,但是整體的平均幀數降了下來。用 Instument 查看時能夠看到 GPU 已經滿負荷運轉,而 CPU 卻比較清閑。為了避免離屏渲染,你應當儘量避免使用 layer 的 border、corner、shadow、mask 等技術,而儘量在後臺線程預先繪製好對應內容。

非同步繪製

我只在顯示文本的控制項上用到了非同步繪製的功能,但效果很不錯。我參考 ASDK 的原理,實現了一個簡單的非同步繪製控制項。這塊代碼我單獨提取出來,放到了這裡:YYAsyncLayer。YYAsyncLayer 是 CALayer 的子類,當它需要顯示內容(比如調用了 [layer setNeedDisplay])時,它會向 delegate,也就是 UIView 請求一個非同步繪製的任務。在非同步繪製時,Layer 會傳遞一個 BOOL(^isCancelled)()這樣的 block,繪製代碼可以隨時調用該 block 判斷繪製任務是否已經被取消。

當 TableView 快速滑動時,會有大量非同步繪製任務提交到後臺線程去執行。但是有時滑動速度過快時,繪製任務還沒有完成就可能已經被取消了。如果這時仍然繼續繪製,就會造成大量的 CPU 資源浪費,甚至阻塞線程並造成後續的繪製任務遲遲無法完成。我的做法是儘量快速、提前判斷當前繪製任務是否已經被取消;在繪製每一行文本前,我都會調用 isCancelled() 來進行判斷,保證被取消的任務能及時退出,不至於影響後續操作。

目前有些第三方微博客戶端(比如 VVebo、墨客等),使用了一種方式來避免高速滑動時 Cell 的繪製過程,相關實現見這個項目:VVeboTableViewDemo。它的原理是,當滑動時,鬆開手指後,立刻計算出滑動停止時 Cell 的位置,並預先繪製那個位置附近的幾個 Cell,而忽略當前滑動中的 Cell。這個方法比較有技巧性,並且對於滑動性能來說提升也很大,唯一的缺點就是快速滑動中會出現大量空白內容。如果你不想實現比較麻煩的非同步繪製但又想保證滑動的流暢性,這個技巧是個不錯的選擇。

全局併發控制

當我用 concurrent queue 來執行大量繪製任務時,偶爾會遇到這種問題:

大量的任務提交到後臺隊列時,某些任務會因為某些原因(此處是 CGFont 鎖)被鎖住導致線程休眠,或者被阻塞,concurrent queue 隨後會創建新的線程來執行其他任務。當這種情況變多時,或者 App 中使用了大量 concurrent queue 來執行較多任務時,App 在同一時刻就會存在幾十個線程同時運行、創建、銷毀。CPU 是用時間片輪轉來實現線程併發的,儘管 concurrent queue 能控制線程的優先順序,但當大量線程同時創建運行銷毀時,這些操作仍然會擠占掉主線程的 CPU 資源。ASDK 有個 Feed 列表的 Demo:SocialAppLayout,當列表內 Cell 過多,並且非常快速的滑動時,界面仍然會出現少量卡頓,我謹慎的猜測可能與這個問題有關。

使用 concurrent queue 時不可避免會遇到這種問題,但使用 serial queue 又不能充分利用多核 CPU 的資源。我寫了一個簡單的工具 YYDispatchQueuePool,為不同優先順序創建和 CPU 數量相同的 serial queue,每次從 pool 中獲取 queue 時,會輪詢返回其中一個 queue。我把 App 內所有非同步操作,包括圖像解碼、對象釋放、非同步繪製等,都按優先順序不同放入了全局的 serial queue 中執行,這樣儘量避免了過多線程導致的性能問題。

更高效的非同步圖片載入

SDWebImage 在這個 Demo 里仍然會產生少量性能問題,並且有些地方不能滿足我的需求,所以我自己實現了一個性能更高的圖片載入庫。在顯示簡單的單張圖片時,利用 UIView.layer.contents 就足夠了,沒必要使用 UIImageView 帶來額外的資源消耗,為此我在 CALayer 上添加了 setImageWithURL 等方法。除此之外,我還把圖片解碼等操作通過 YYDispatchQueuePool 進行管理,控制了 App 匯流排程數量。

其他可以改進的地方

上面這些優化做完後,微博 Demo 已經非常流暢了,但在我的設想中,仍然有一些進一步優化的技巧,但限於時間和精力我並沒有實現,下麵簡單列一下:

列表中有不少視覺元素並不需要觸摸事件,這些元素可以用 ASDK 的圖層合成技術預先繪製為一張圖。

再進一步減少每個 Cell 內圖層的數量,用 CALayer 替換掉 UIView。

目前每個 Cell 的類型都是相同的,但顯示的內容卻各部一樣,比如有的 Cell 有圖片,有的 Cell 里是卡片。把 Cell 按類型劃分,進一步減少 Cell 內不必要的視圖對象和操作,應該能有一些效果。

把需要放到主線程執行的任務劃分為足夠小的塊,並通過 Runloop 來進行調度,在每個 Loop 里判斷下一次 VSync 的時間,併在下次 VSync 到來前,把當前未執行完的任務延遲到下一個機會去。這個只是我的一個設想,並不一定能實現或起作用。

如何評測界面的流暢度

最後還是要提一下,“過早的優化是萬惡之源”,在需求未定,性能問題不明顯時,沒必要嘗試做優化,而要儘量正確的實現功能。做性能優化時,也最好是走修改代碼 -> Profile -> 修改代碼這樣一個流程,優先解決最值得優化的地方。

如果你需要一個明確的 FPS 指示器,可以嘗試一下 KMCGeigerCounter。對於 CPU 的卡頓,它可以通過內置的 CADisplayLink 檢測出來;對於 GPU 帶來的卡頓,它用了一個 1x1 的 SKView 來進行監視。這個項目有兩個小問題:SKView 雖然能監視到 GPU 的卡頓,但引入 SKView 本身就會對 CPU/GPU 帶來額外的一點的資源消耗;這個項目在 iOS 9 下有一些相容問題,需要稍作調整。

我自己也寫了個簡單的 FPS 指示器:FPSLabel 只有幾十行代碼,僅用到了 CADisplayLink 來監視 CPU 的卡頓問題。雖然不如上面這個工具完善,但日常使用沒有太大問題。

最後,用 Instuments 的 GPU Driver 預設,能夠實時查看到 CPU 和 GPU 的資源消耗。在這個預設內,你能查看到幾乎所有與顯示有關的數據,比如 Texture 數量、CA 提交的頻率、GPU 消耗等,在定位界面卡頓的問題時,這是最好的工具。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • sqlserver 單表(視圖)通用分頁存儲過程 create procedure proc_getpage @table_name varchar(100), --表名(視圖) @select_fields varchar(1000)=' * ', --需要返回的列 @page_size int= ...
  • oracle體繫結構簡介 一、物理存儲結構 1、數據文件 存放資料庫數據,以dbf為擴展名。將數據放在多個數據文件中, 再將數據文件分放在不同的硬碟中,可以提高存取速度。數據文 件由數據塊構成,塊大小由資料庫創建時確定。 2、重做日誌文件,以rdo為擴展名。含對資料庫所做的更改記錄, 這樣萬一齣現故 ...
  • Hive的文件存儲格式: textFile textFile為預設格式 存儲方式:行存儲 缺點:磁碟開銷大;數據解析開銷大;壓縮的text文件,hive無法進行合併和拆分 sequencefile 二進位文件,以<key,value>的形式序列化到文件中 存儲方式:行存儲 優點:可分割、壓縮,一般選 ...
  • 文中Zookeeper分散式集群搭建在Linux CentOS7系統之上。搭建大數據分散式集群,不建議使用root用戶直接操作,故文中使用bigdata用戶進行集群的搭建。 一、環境準備 軟體 版本 Linux系統 CentOS7 JDK jdk-8u221-linux-x64.tar.gz Zoo ...
  • centos7線上yum安裝mysql時官方鏡像下載過慢的一個解決方案 ...
  • sqlserver 用於刷新當前資料庫所有視圖的存儲過程 create procedure dbo.proc_refreshview as begin declare @viewname varchar(100) declare cur_view cursor for select [name] f ...
  • 一、文件操作相關命令 1.文件操作命令 子命令參數說明 cd 無 進入目錄 cat [-beflnstuv] [-B bsize] [file...] 查看文件內容-n:顯示行號-b:顯示行號,但會忽略空行-s:顯示行號,連續空行標記為一行 df 無 列出分區列表 du [-H] [-L] [-P] ...
  • 在上一篇文章中我們分享了,如何開發桌面應用。在本章文章中,來解決一下為何在 Mac 中無法發出網路情況的原因。## 起因事情​起因是這樣的:我總覺得寫一個 Demo 不足以體現我們開發同學的能力。直到最近,我發現了一個可以改善的小點,可幫助我們的測試同學提高測試效率。大體情況就是在某天晚上,客戶端的... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...