iOS優化(三)沒錯我還是滑動優化

近期把滑動優化的一些經驗整理了一下,在公司做了一次技術分享,和我之前的文章有一小部分重疊。現摘要如下,希望大家不吝賜教,共同討論進步。

一.滑動優化的玄學

為什么說是玄學呢,因為大部分情況下的APP,用不到這些優化的點,過早的優化是惡魔,當真正出現性能問題的時候,再考慮這些方面的優化。

1.多個透明元素重疊顯示的性能問題。

  • 解決方案:合并成一張圖顯示
  • 原理:CPU方面,減少了UIKit的創建消耗,GPU方面,避免了合成渲染產生的消耗。
  • AsyncDisplayKit(現在叫Texture),針對多個透明元素的重疊,預合并無點擊響應,不改變動畫的圖層。
  • Texture的保持流暢的原理:UIKit不是線程安全的,所以必須在主線程改動。Texture利用中間變量存儲改動,保證線程安全,在合適的機會將并發操作同步到主線程。
  • 暫時不用Texture的原因:需要用Texture Node Container替換UIKit元素,成本較大。

2.靜態cell、多圖待加載的優化

  • 解決方案:合并成一張圖顯示;
  • 原理:提升I/O速度,一個大文件的讀取速度,通常比多個小文件要快。

3.展示適合界面尺寸圖片,不進行拉伸縮放。

  • 解決方案:從服務器拉取合適尺寸的圖片(例如七牛的服務就帶裁剪/壓縮參數);
  • 原理:過大圖片對內存消耗巨大(圖片占用內存 = 圖像高×圖像寬×像素位數);不符合UIImageView尺寸的圖片,進行重新縮減/放大尺寸的消耗是非常巨大的。

4.imageNamed和imageWithContentsOfFile

這個知道的人比較多,因為緩存圖片的消耗通常是肉眼可見的多。
常用的元素例如icon之類的,采用imageNamed:,系統會有緩存。
如果是較大或者不常用的圖片資源,采用imageWithContentsOfFile:。

5.減少autolayout的使用

  • 解決方案:頁面元素多的時候,減少autolayout布局,采用frame。
  • 原理:元素多時,autolayout的消耗非常驚人(http://pilky.me/36/) ,之前看過搜狗的iOS分享,搜狗輸入法鍵盤彈出狂卡即是此原因;

6.獲取文件大小

  • 解決方案:不要使用NSFileManager,用C的stat來獲取文件信息。
  • 實例:獲取一個目錄下所有文件大小,進行多次遞歸計算,stat幾乎瞬間完成,NSFileManager耗時較長。

7.NSDateFormatter產生較大消耗

  • 解決方案:.緩存NSDateFormatter結果,不多次創建,及時釋放。
  • 做過類似日歷的同學應該都懂??

8.圖片解碼:

  • 解決方案:CALayer 被提交到 GPU 前,CGImage 中的數據才會得到解碼,GPU執行,卡主線程。常見的做法是在后臺線程先把圖片繪制到 CGBitmapContext 中,然后從 Bitmap 直接創建圖片。
  • SDWebImage/YYImage等圖片庫都是這么做的,有興趣的同學可以去看下源碼。如果你是自己做圖片下載,就要考慮到相關優化。

二.cell高度預計算/緩存

  • 一般情況下,不要用estimatedRowHeight,不然容易鬼畜;
  • systemLayoutSizeFittingSize:這個方法,就是大部分cell布局庫采用的方法,只要從上至下布局全部生效,就能計算高度,不要多次調用;
  • 由于UITableView繪制過程中多次調用繪制,所以緩存高度計算結果,可以有效的增加滑動流暢度;
  • 當cell高度改變,記得及時替換緩存;

三.離屏渲染

觸發條件:CALayer 的 border、圓角、陰影、遮罩(mask),CASharpLayer 的矢量圖形顯示。
主要問題:GPU占滿,CPU空閑
解決方法:
1.開啟CALayer.shouldRasterize ,轉嫁到CPU上;
2.粗暴畫圖/截圖實現border和圓角;
3.砍死設計師。

最近拖延癥有點厲害,這篇文章想寫了很久都沒寫出來,我先摘要一部分思路出來。


拖延癥晚期患者.png

曾經的需求是這樣的:


注意需求的圈和頭像之間是有空隙的.png

初期方案是圖片下載完成后,裁成圓形,然后外面用貝塞爾畫一個圈,根據不同的UI緩存不同多個帶圈兒的圖;
然而...神奇的產品第二版給我整出了無數個各種不同大小、間距、透明度的圈。這樣就意味著我得緩存無數帶著各色圈兒的圖。

此時的心情.png

后來突然靈光一閃,單獨設layer的圓角貌似不引發離屏渲染(有說法是iOS8之后)。所以后來方案變成,UIView嵌套一個UIImageView,只緩存裁剪過的圖片。

但是這樣的話,其實UIKit的創建要比讀取畫好的圖更耗性能。所以這是個終極的問題,時間/空間,究竟選哪個。

四.圖片的處理

1.SDWebImage的外層干了啥

喜聞樂見的拖延癥晚期

這篇文章寫了1/4的樣子,詳細的會在這篇文章里,十分心疼自己。下面簡單說說SDWebImage的外層都干了啥:

  • 重用cell的時候,cancel之前下載operation;
  • 二級緩存:memory/disk;
  • 合并回調,多次調用只回調一次;

2.項目中實際遇到的問題

我們做了個類似SDWebImage的東西,來實現神奇的需求,過程中遇到了一些問題:

  • 從disk讀取需要時間,在memory沒有的情況下,導致image = nil的一瞬間閃動:我把SDWeb的demo改了改,左邊按鈕ReloadData,右邊按鈕清除內存(memory)緩存,在reload的一瞬間,展示了placeHolder的圖。


    SDWebFlash.gif
  • UICollectionView, reloadData 迷之移位閃爍問題:當reloadData的時候,重用的cell神奇的變換了次序,此時memory里只要一圖片不存在,就會有一瞬間的閃動。

  • 上面兩個問題的解決方法有:

    • 盡量避免reloadData,可以找到visibleCells,一一更換換圖片;
    • 優化細節,在發現disk里面有圖的時候,image不設為nil,避免閃動;
    • 保證memory緩存的大小和數量,能滿足界面需求;
  • 請求沒有合并/回調沒有合并;
    這個比較好解決,就是請求之前判斷該請求是否在執行,若在執行,則將回調暫存到該請求下,等完成后一并調用。

五.ScrollView滑動優化

1.滑動時的代理

  • scrollViewDidScroll:實際上就是contentOffset的KVO
  • scrollViewWillBeginDragging:
  • scrollViewWillEndDragging: withVelocity: (points/millisecond這實際上是個速度的參數)targetContentOffset:(這是一個可以傳值的指針,可以控制最后的減速動畫)
  • scrollViewDidEndDragging: willDecelerate:(拖動結束,如果仍有速度,會執行后面兩個方法)
  • scrollViewWillBeginDecelerating:
  • scrollViewDidEndDecelerating:
  • 需要注意scrollView的dragging屬性在decelerate的過程中仍然為YES

2.特殊情況

drag完,正decelerating時(didEndDecelerating尚未調用),強行再次drag(單指停止滑動,雙指連環滑)

  • 單指停止滑動:沒有decelerate ,willBeginDecelerating不會被調用,但前一次留下的 didEndDecelerating 會被調用(后面會結合VVWeibo的例子講述這里怎么處理)
  • 雙指連環滑動:willBeginDecelerating會先于didEndDecelerating調用,就是說這種情況didEndDecelerating會在你手指離開屏幕且屏幕停止的情況下調用。

3.VVWeibo的做法

  • scrollViewWillEndDragging: withVelocity: targetContentOffset:時,可以從targetContentOffset判斷即將加載的那一頁cell,從而預先加載,UITableView有傳入rect返回cells的方法,UICollectionView得強行取兩個點獲得這兩個點cell的IndexPath,然后得到cells。
  • 遇到前文單指停止的處理,VVWeibo是在UITableView的子類捕獲了touchEvent,然后reloadData,我就沒有做子類了。最后做了一系列神奇的判斷,然后reload。但是仍然遇到了 reloadData 迷之移位閃爍問題;后來我加入了速度的判斷,這個已經不會觸發了,我就暫時注銷掉了,等待下一步優化。

4.最迷的問題

UICollectionViewLayout的prepareLayout調用了過多次數,是因為shouldInvalidateLayoutForBoundsChange:這個方法災難的調用了多次,newBounds的x和y實際上隨著滑動一直在變,return YES的話就一直重新布局,最后用magicNumber存他的size,當size變化才返回YES,就很強行的解決了。

六.魔鬼般的視頻播放

這里涉及業務邏輯過多,我也不方便多寫,就寫一些過程中遇到的問題:

  • scrollViewDidEndDecelerating的VisibleItems為nil。換個線程/延時去取,就能取到。

  • 縮小播放區域,跟前面的取點找目標cell的操作類似,找出首尾點,中間的cell,即是需要播放的cell。

  • 多個等待播放的AVPlayerItemVideoOutput,會導致一部分失效,內存越小的機子上越明顯。
    解決方法:播放前再把url賦給playerItem,一定程度避免過多playerItem失敗的問題;

  • 出入頁面找到所有playerItem并干掉,避免影響其他播放;

  • GLKView的reuse在狂滑的時候十分耗內存,而不reuse的話,重用的時候,會顯示上一個頁面的殘影,解決方法是先用圖片蓋住殘影,在播前,清理上一次播放的殘余;

  • 加入一個Timer,通過記錄偏移量來控制滑動速度,高速度的時候,不繪制/下載圖片。這樣也解決了雙指狂滑的時候,無法很好的判斷當前是否繪制的問題。

Conference

http://wereadteam.github.io/2016/05/03/WeRead-Performance/ 微信讀書 iOS 性能優化總結
http://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/ iOS 保持界面流暢的技巧
http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/ 優化UITableViewCell高度計算的那些事
http://tech.glowing.com/cn/practice-in-uiscrollview/ UIScrollView 實踐經驗
《High Performance iOS Apps》這本真是神書,有興趣深入學習優化的可以去看看,中文的貌似有美團技術團隊翻譯的

簡書已經棄用,歡迎移步我的小專欄:
https://xiaozhuanlan.com/dahuihuiiOS

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

推薦閱讀更多精彩內容