前言web
隨着APP功能的逐漸強大和業務上的逐漸完善,目前對於iOS開發者來講,對於APP的優化逐漸顯得尤其重要,本篇基於APP渲染優化上探討一下imageName:的愛恨情仇,下面以UITabBarItem渲染圖片爲例,一步步以實踐的方式進行分析。緩存
分析異步
首先看下未優化前的效果圖:async
測試設備:iPhone7 ,系統:12.1函數
細心的同窗應該可以發現,在登陸進入首頁,到首頁渲染結束,中間會有一段白屏,爲何會白屏一會而沒有立刻渲染首頁呢,第一感受確定是這中間造成主線程阻塞了,讓UI沒有當即渲染出來,其實事實上確實是這樣,那接下來咱們經過Instruments分析一下哪裏執行了耗時操做以致於首頁渲染被阻塞了。Instruments裏面有個工具TimeProfiler,能夠用來幫咱們查看哪裏有耗時操做。關於這個工具的使用和配置網上不少介紹本篇不作重點分析了,我直接粘調試的圖片了。工具
經過TimeProfiler的結果一目瞭然,在CustomTabBarItem裏面作了什麼用了387ms。能夠在工具裏面直接右鍵進入到到這段耗時代碼的位置。我總共測試了五個tabbar渲染item圖片的耗時:測試
看打點日誌就很恐怖了,執行兩個imageName:就消耗了主線程差很少100ms的時間,五個tabbar那就是500ms的時間,顯然這就是上面效果圖出現白屏的緣由了,實際上imageName:是會對圖片進行解碼以後再渲染的。優化
既然緣由找到了,那就嘗試解決一下。將這個耗時的操做放到子線程執行,這裏也是參考了SDWebImage的圖片編解碼的思路,SD在拿到圖片data的時候並無將它直接轉爲image對象,而是在子線程裏面作了一個解碼的操做,這樣已經被解碼的圖片就賦值給imageView的時候就不會再進行解碼,也就不會妨礙主線程了。ui
- (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",具體實現網上對這一塊的源碼解釋的比較多,很容易理解。spa
這樣咱們的圖片通過這層處理以後,咱們再來看一下優化以後的效果:
從總計500ms降到了6ms,基本能夠忽略不及了,咱們再在真機上面看一下優化後的效果:
通過同事的提醒和評論區小夥伴的質疑,我又從新測試了一下imageName:的加載耗時,獲得一種現象,若是將圖片資源放入在文件目錄中,每張圖片第一次加載依舊耗時大概30ms左右,由於imageName:方法對圖片有緩存,因此測試屢次加載須要多張不一樣的圖片進行測試。另外我將測試圖片放入Assets.xcassets中每張圖片加載耗時大概在1ms左右,猜想Assets.xcassets提早將圖片作了緩存。
測試圖片:
總結
經過上面的分析,實際上imageName:這樣的UI函數咱們每天都在用,可是從沒想過它在某些地方能產生這麼大的影響。問題的定位和解決其實都很簡單,可是這種簡單的問題每每會被咱們開發者忽略掉,產生一些很差的結果,值得反思。