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

近期把滑動優(yōu)化的一些經(jīng)驗整理了一下澄成,在公司做了一次技術(shù)分享,和我之前的文章有一小部分重疊∥废牛現(xiàn)摘要如下环揽,希望大家不吝賜教,共同討論進(jìn)步庵佣。

一.滑動優(yōu)化的玄學(xué)

為什么說是玄學(xué)呢,因為大部分情況下的APP汛兜,用不到這些優(yōu)化的點巴粪,過早的優(yōu)化是惡魔,當(dāng)真正出現(xiàn)性能問題的時候,再考慮這些方面的優(yōu)化肛根。

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

  • 解決方案:合并成一張圖顯示
  • 原理:CPU方面,減少了UIKit的創(chuàng)建消耗派哲,GPU方面臼氨,避免了合成渲染產(chǎn)生的消耗。
  • AsyncDisplayKit(現(xiàn)在叫Texture)芭届,針對多個透明元素的重疊储矩,預(yù)合并無點擊響應(yīng),不改變動畫的圖層褂乍。
  • Texture的保持流暢的原理:UIKit不是線程安全的持隧,所以必須在主線程改動。Texture利用中間變量存儲改動逃片,保證線程安全屡拨,在合適的機(jī)會將并發(fā)操作同步到主線程。
  • 暫時不用Texture的原因:需要用Texture Node Container替換UIKit元素褥实,成本較大呀狼。

2.靜態(tài)cell、多圖待加載的優(yōu)化

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

3.展示適合界面尺寸圖片她奥,不進(jìn)行拉伸縮放。

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

4.imageNamed和imageWithContentsOfFile

這個知道的人比較多凡资,因為緩存圖片的消耗通常是肉眼可見的多。
常用的元素例如icon之類的谬运,采用imageNamed:隙赁,系統(tǒng)會有緩存。
如果是較大或者不常用的圖片資源梆暖,采用imageWithContentsOfFile:伞访。

5.減少autolayout的使用

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

6.獲取文件大小

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

7.NSDateFormatter產(chǎn)生較大消耗

  • 解決方案:.緩存NSDateFormatter結(jié)果,不多次創(chuàng)建诱咏,及時釋放苔可。
  • 做過類似日歷的同學(xué)應(yīng)該都懂??

8.圖片解碼:

  • 解決方案:CALayer 被提交到 GPU 前,CGImage 中的數(shù)據(jù)才會得到解碼袋狞,GPU執(zhí)行焚辅,卡主線程。常見的做法是在后臺線程先把圖片繪制到 CGBitmapContext 中苟鸯,然后從 Bitmap 直接創(chuàng)建圖片同蜻。
  • SDWebImage/YYImage等圖片庫都是這么做的,有興趣的同學(xué)可以去看下源碼早处。如果你是自己做圖片下載湾蔓,就要考慮到相關(guān)優(yōu)化。

二.cell高度預(yù)計算/緩存

  • 一般情況下砌梆,不要用estimatedRowHeight默责,不然容易鬼畜;
  • systemLayoutSizeFittingSize:這個方法咸包,就是大部分cell布局庫采用的方法桃序,只要從上至下布局全部生效,就能計算高度烂瘫,不要多次調(diào)用媒熊;
  • 由于UITableView繪制過程中多次調(diào)用繪制,所以緩存高度計算結(jié)果坟比,可以有效的增加滑動流暢度芦鳍;
  • 當(dāng)cell高度改變,記得及時替換緩存葛账;

三.離屏渲染

觸發(fā)條件:CALayer 的 border柠衅、圓角、陰影籍琳、遮罩(mask)菲宴,CASharpLayer 的矢量圖形顯示魂贬。
主要問題:GPU占滿,CPU空閑
解決方法:
1.開啟CALayer.shouldRasterize 裙顽,轉(zhuǎn)嫁到CPU上;
2.粗暴畫圖/截圖實現(xiàn)border和圓角宣谈;
3.砍死設(shè)計師愈犹。

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


拖延癥晚期患者.png

曾經(jīng)的需求是這樣的:


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

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

此時的心情.png

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

但是這樣的話琼牧,其實UIKit的創(chuàng)建要比讀取畫好的圖更耗性能恢筝。所以這是個終極的問題,時間/空間巨坊,究竟選哪個撬槽。

四.圖片的處理

1.SDWebImage的外層干了啥

喜聞樂見的拖延癥晚期

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

  • 重用cell的時候,cancel之前下載operation鼓寺;
  • 二級緩存:memory/disk勋拟;
  • 合并回調(diào),多次調(diào)用只回調(diào)一次妈候;

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

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

  • 從disk讀取需要時間苦银,在memory沒有的情況下啸胧,導(dǎo)致image = nil的一瞬間閃動:我把SDWeb的demo改了改赶站,左邊按鈕ReloadData,右邊按鈕清除內(nèi)存(memory)緩存纺念,在reload的一瞬間贝椿,展示了placeHolder的圖。


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

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

    • 盡量避免reloadData渣窜,可以找到visibleCells,一一更換換圖片宪躯;
    • 優(yōu)化細(xì)節(jié)乔宿,在發(fā)現(xiàn)disk里面有圖的時候,image不設(shè)為nil访雪,避免閃動详瑞;
    • 保證memory緩存的大小和數(shù)量,能滿足界面需求冬阳;
  • 請求沒有合并/回調(diào)沒有合并蛤虐;
    這個比較好解決,就是請求之前判斷該請求是否在執(zhí)行肝陪,若在執(zhí)行驳庭,則將回調(diào)暫存到該請求下,等完成后一并調(diào)用氯窍。

五.ScrollView滑動優(yōu)化

1.滑動時的代理

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

2.特殊情況

drag完贝淤,正decelerating時(didEndDecelerating尚未調(diào)用),強(qiáng)行再次drag(單指停止滑動政供,雙指連環(huán)滑)

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

3.VVWeibo的做法

  • scrollViewWillEndDragging: withVelocity: targetContentOffset:時,可以從targetContentOffset判斷即將加載的那一頁cell衅檀,從而預(yù)先加載招刨,UITableView有傳入rect返回cells的方法,UICollectionView得強(qiáng)行取兩個點獲得這兩個點cell的IndexPath哀军,然后得到cells沉眶。
  • 遇到前文單指停止的處理打却,VVWeibo是在UITableView的子類捕獲了touchEvent,然后reloadData谎倔,我就沒有做子類了柳击。最后做了一系列神奇的判斷,然后reload片习。但是仍然遇到了 reloadData 迷之移位閃爍問題腻暮;后來我加入了速度的判斷,這個已經(jīng)不會觸發(fā)了毯侦,我就暫時注銷掉了,等待下一步優(yōu)化具垫。

4.最迷的問題

UICollectionViewLayout的prepareLayout調(diào)用了過多次數(shù)侈离,是因為shouldInvalidateLayoutForBoundsChange:這個方法災(zāi)難的調(diào)用了多次,newBounds的x和y實際上隨著滑動一直在變筝蚕,return YES的話就一直重新布局卦碾,最后用magicNumber存他的size,當(dāng)size變化才返回YES起宽,就很強(qiáng)行的解決了洲胖。

六.魔鬼般的視頻播放

這里涉及業(yè)務(wù)邏輯過多,我也不方便多寫坯沪,就寫一些過程中遇到的問題:

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

  • 縮小播放區(qū)域叉弦,跟前面的取點找目標(biāo)cell的操作類似,找出首尾點藻糖,中間的cell淹冰,即是需要播放的cell。

  • 多個等待播放的AVPlayerItemVideoOutput巨柒,會導(dǎo)致一部分失效樱拴,內(nèi)存越小的機(jī)子上越明顯。
    解決方法:播放前再把url賦給playerItem洋满,一定程度避免過多playerItem失敗的問題晶乔;

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

  • GLKView的reuse在狂滑的時候十分耗內(nèi)存瘪弓,而不reuse的話,重用的時候禽最,會顯示上一個頁面的殘影腺怯,解決方法是先用圖片蓋住殘影袱饭,在播前,清理上一次播放的殘余呛占;

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

Conference

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

簡書已經(jīng)棄用笙隙,歡迎移步我的小專欄:
https://xiaozhuanlan.com/dahuihuiiOS

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末洪灯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子竟痰,更是在濱河造成了極大的恐慌签钩,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坏快,死亡現(xiàn)場離奇詭異铅檩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)莽鸿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門昧旨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人祥得,你說我怎么就攤上這事臼予。” “怎么了啃沪?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵粘拾,是天一觀的道長。 經(jīng)常有香客問我创千,道長缰雇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任追驴,我火速辦了婚禮械哟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘殿雪。我一直安慰自己暇咆,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著爸业,像睡著了一般其骄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扯旷,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天拯爽,我揣著相機(jī)與錄音,去河邊找鬼钧忽。 笑死毯炮,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的耸黑。 我是一名探鬼主播桃煎,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼大刊!你這毒婦竟也來了备禀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤奈揍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后赋续,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體男翰,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年纽乱,在試婚紗的時候發(fā)現(xiàn)自己被綠了蛾绎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡鸦列,死狀恐怖租冠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情薯嗤,我是刑警寧澤顽爹,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站骆姐,受9級特大地震影響镜粤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜玻褪,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一肉渴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧带射,春花似錦同规、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绪钥。三九已至,卻和暖如春朱灿,著一層夾襖步出監(jiān)牢的瞬間昧识,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工盗扒, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留跪楞,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓侣灶,卻偏偏與公主長得像甸祭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子褥影,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

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