前言
隨著APP功能的逐漸強大和業務上的逐漸完善,目前對於iOS開發者來說,對於APP的優化逐漸顯得尤為重要,本篇基於APP渲染優化上探討一下imageName:
的愛恨情仇,下面以UITabBarItem
渲染圖片為例,一步步以實踐的方式進行分析。
分析
首先看下未優化前的效果圖:
測試裝置:iPhone7 ,系統:12.1
細心的同學應該能夠發現,在登入進入首頁,到首頁渲染結束,中間會有一段白屏,為什麼會白屏一會而沒有馬上渲染首頁呢,第一感覺肯定是這中間形成主執行緒阻塞了,讓UI沒有立即渲染出來,其實事實上確實是這樣,那接下來我們通過Instruments
分析一下哪裡執行了耗時操作以至於首頁渲染被阻塞了。Instruments
裡面有個工具TimeProfiler
,可以用來幫我們檢視哪裡有耗時操作。關於這個工具的使用和配置網上很多介紹本篇不做重點分析了,我直接粘除錯的圖片了。
通過TimeProfiler的結果一目瞭然,在CustomTabBarItem裡面做了什麼用了387ms。可以在工具裡面直接右鍵進入到到這段耗時程式碼的位置。我總共測試了五個tabbar渲染item圖片的耗時:
看打點日誌就很恐怖了,執行兩個imageName:
就消耗了主執行緒差不多100ms的時間,五個tabbar
那就是500ms
的時間,顯然這就是上面效果圖出現白屏的原因了,實際上imageName:
是會對圖片進行解碼之後再渲染的。
既然原因找到了,那就嘗試解決一下。將這個耗時的操作放到子執行緒執行,這裡也是參考了SDWebImage
的圖片編解碼的思路,SD
在拿到圖片data
的時候並沒有將它直接轉為image
物件,而是在子執行緒裡面做了一個解碼的操作,這樣已經被解碼的圖片就賦值給imageView
的時候就不會再進行解碼,也就不會妨礙主執行緒了。
- (void)decodedImageWithImageName:(NSString *)imageName block:(void(^)(UIImage *image))block {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
@autoreleasepool{
UIImage *image = [[UIImage imageNamed:imageName] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
image = [UIImage decodedImageWithImage:image];
dispatch_async(dispatch_get_main_queue(), ^{
if(block)
block(image);
});
}
});
}
複製程式碼
程式碼實現很簡單,就是將圖片的操作放入到一個全域性佇列中,當然也可以自己建立一個佇列去執行這個非同步操作。decodedImageWithImage:
為SD的程式碼,需要#import "SDWebImageDecoder.h"
,具體實現網上對這一塊的原始碼解釋的比較多,很容易理解。
這樣我們的圖片經過這層處理之後,我們再來看一下優化之後的效果:
從總計500ms降到了6ms,基本可以忽略不及了,我們再在真機上面看一下優化後的效果:經過同事的提醒和評論區小夥伴的質疑,我又重新測試了一下
imageName:
的載入耗時,得到一種現象,如果將圖片資源放入在檔案目錄中,每張圖片第一次載入依舊耗時大概30ms
左右,因為imageName:
方法對圖片有快取,所以測試多次載入需要多張不同的圖片進行測試。另外我將測試圖片放入Assets.xcassets
中每張圖片載入耗時大概在1ms
左右,猜測Assets.xcassets
提前將圖片做了快取。
測試圖片:
總結
通過上面的分析,實際上imageName:
這樣的UI
函式我們天天都在用,但是從沒想過它在某些地方能產生這麼大的影響。問題的定位和解決其實都很簡單,但是這種簡單的問題往往會被我們開發者忽略掉,產生一些不好的結果,值得反思。