今天博客的內容就係統的討論一下Masonry對FSP的影響,以及如何更好的使用Masonry。若是你對iOS開發足夠熟悉的話,那麼對Masonry框架應該不陌生。簡單的說,Masonry的誕生讓AutoLayout的使用更爲優雅,讓控件的佈局更爲方便。使用辯證的觀點來看一個事物的話,凡事都有兩面性,Masonry的使用也不例外。Masonry框架的使用不當會直接影響當UI的FPS。今天咱們就來討論一下在使用Masonry時的一些誤區,看一下那些影響性能的使用方式。本篇博客咱們依然會依託於Demo來敘述的一些東西。git
1、Demo綜述github
1.運行效果數組
先入爲主,本篇博客的內容依然是依託於咱們特地爲本篇博客所打造的Demo的,首先咱們先來看一下Demo運行起來是怎樣的效果,經過Demo咱們能夠看到那些問題,以及這些問題是如何被解決的。下方就是咱們本篇博客所涉及Demo的運行效果。網絡
從下方的運行效果不難看出,咱們是分了6種狀況來觀察和判斷Masonry的各類使用方式對FPS的影響如何。上方經過六個SegmentControl能夠去切換Cell的佈局方式。固然每種佈局方式所呈現出來的Cell是相同的。這樣也是作實驗時保持實驗項改變其餘項保持一致的原則。咱們能夠經過右下方FPS指示器來直觀的感覺一下FPS的變化趨勢。下方這個FPS顯示控件是從咱們以前的Demo中拿過來的。以前的Demo也是關於FPS優化的,只不過是關於Cell高度動態計算的FPS優化,詳情請移步於《iOS開發之多種Cell高度自適應實現方案的UI流暢度分析》。框架
下方Cell中所顯示的數據時隨機生成的,左邊的Image也是隨機取的。右邊的Title和Detail都是NSAttributedString而且下方的有些Detail有可能爲空。若是某一條的Detail爲空,那麼該條Detail下方的全部內容的佈局上移。稍後會詳細的介紹該Demo以及其中的技術點。oop
二、模擬網絡請求佈局
上面Cell中顯示的數據是經過模擬網絡數據來獲取的,下方就是咱們的模擬網絡層的相關代碼。畢竟是Demo,而且Demo的重點不在網絡層上,下方就簡單的寫了一下,代碼比較簡單。就是一個單例+一個模擬數據隨機生成的方法,而後把這個隨機生成的數據經過Block回調到網絡層的使用者上。具體代碼以下所示:性能
三、上述Cell的基類XBaseTableViewCell優化
上面每種Segment中的Cell都是一種獨立的Cell類型,不過這些Cell有一個共同的父類。而這個父類就負責來處理這些Cell所共用的邏輯。下方的這個XBaseTableViewCell就是上述顯示的全部Cell的基類。其中聲明並初始化了Cell上的全部控件。而且提供了相關的設置值的方法。3d
從下方的代碼中不難看出,有兩個方法是須要子類進行重寫的,一個是給控件添加布局的方法addLayoutSubviews, 另外一個就是更新佈局的方法updateLayoutSubviews方法。每一個子類中都會對這兩個方法進行重寫並給出不一樣的佈局方式。
四、Segment中切換Cell的代碼
下方是在相應的VC中的點擊SegmentControl的邏輯代碼, 點擊不一樣的Segment會選擇不一樣的Cell而後刷新TableView,代碼比較簡單就不作過多的贅述了。
2、對上述各類的佈局方式進行分析
接下來要作的事情就是分析一下每種佈局方式對FSP的影響,下方會對不一樣的佈局狀況使用Instrument進行分析並看一下具體的數據。下方分別討論了只使用Masonry的Update操做、Remake操做、先Make後Update、Frame操做以及先Make後Frame操做。
一、update
首先咱們來看一下update操做。也就是使用update直接給控件賦值,這是比較偷懶的一種操做。由於在咱們的Demo中在設置cell的值時會更新一些控件的UI佈局,全部咱們索性就直接使用Masonry的update,直接給控件添加約束。在Masonry中的update操做有個特色,就是update一個約束會先在已添加的約束數組中找到該約束,而後更新該約束,若是找不到就install添加相應的約束。從這個update的功能來看其效率是比較低的。
我能夠先看一下代碼實現,在子類XUpdateLayoutTableViewCell中,重寫了addLayoutSubviews和updateLayoutSubviews兩個方法。在updateLayoutSubviews方法中,爲全部的控件使用update的方式添加約束。下方這樣寫會在每次設置值的時候都會調用下方的updateLayoutSubviews方法,這樣就會更新cell上的控件的全部佈局,固然,不建議這樣去作,由於這樣會更新那些不須要更新的約束。之因此今天羅列出來,是由於在開發中下方的問題確實存在,也許是由於時間緊張,也許是由於其餘緣由致使的下方這種代碼實現。
咱們先來使用Instruments跑一下上述的Demo,而後直觀的感覺一下該Demo的Core Animation的直觀表現。下方就是咱們將SegmentControl切換到Update時所對應的FPS數據。從下方的數據咱們不難看出,直接用Update添加更新約束這種作法是比較影響FPS的。固然,Cell中還會使用到屬性字符串,這個咱們稍後會討論一下的。
咱們能夠來跑一下Update狀態下的Time Profile。以下所示,從下方的結果中不難看出,在Cell更新數據時,有兩塊的操做比較耗時。一個是Masonry的update操做,另外一個則是Label設置NSAttributedString的操做。由於咱們使用的每一個Label都會賦值一個屬性字符串,這個是比較耗時的操做。還有一個要明確一點的是,屬性字符串的建立和生成並不會佔用多少時間,而屬性字符串的賦值和渲染所佔用的時間是比較多的,這一點從下方的Time Profile中也是不難看出的。
二、remake
接下來咱們在來看一下Remake操做,從下方的Core Animation的結果中不難看出,其所表現出來的效果還不如使用Update操做呢。下方的FPS比Update要低一些,這也與remake自身的操做有關係,remake從字面意思來看就是從新制做,若是以前已經添加過約束的話就先移除掉,而後再添加新的約束。
下方是Remake所對應的Time Profile,從結果中咱們能夠看出佈局更新佔用了66.6%的耗時,並且33%的install耗時中uninstall佔用了10%左右的開銷。在Masonry中remake效率是最低的。稍後咱們會繼續進行討論。
三、make + update
討論完update和remake, 咱們來討論一下使用Masonry的常規作法。就是使用make來初始化控件的佈局,使用update來更新所須要更新的約束。由於代碼比較簡單,就不一一往上貼了,可是跑一下使用Instrument跑一下仍是頗有必要的。下方是make + update 的方式的Core Animation所跑出來的結果。但從下方的FSP結果來看,仍是要比以前只使用update或者remake的效果要好一些的,不過下方的FPS仍是不高,稍後咱們會將下方的數據進行細化。
該部分的Time Profile就不跑了,由於設置值的時候咱們依然採用的Update來更新的約束,只不過不是更新全部的約束,而是更新那些只須要更新的約束。由於更新的約束的量會少一些,全部FPS的表現效果會比以前更新全部的約束會更好一些。make + update的方式會是FPS稍微改善一些,可是從下方的圖中咱們能夠看出,改善的並非特別好。
四、frame + frame
接下來,咱們就不用Masonry來佈局了,咱們直接使用Frame佈局。由於Autolayout最終仍然會轉換爲Frame佈局的,很顯然Frame佈局在性能方面是優於Autolayout佈局的。接下來咱們就來使用Frame佈局而後使用Frame更新。下方的FPS還算說得過去,不過還不是滿格,其大部分緣由就是由於NSAttrubitedString的緣由了。
咱們能夠看一下更新Frame的Time Profile,以下所示。從下方的截圖中,咱們不難看出update frame的時間佔比只佔到了2.5%,以前更新約束能佔到60%左右,能夠看到使用Frame佈局的好處。從下方的分析結果中不難看出,如今影響FPS主要因素已經從更新佈局轉化到了AttributeString的設置。這也是上述FPS沒有滿格的緣由。
五、make + frame
Masonry的誕生就是爲了方便控件佈局的,Frame佈局不夠靈活,適配起來比較繁瑣,因此纔有了AutoLayout。不過雖然AutoLayout能夠很方便的適配屏幕,但是其性能方面表現的不是特別好。那麼咱們可不能夠將二者進行結合呢。也就是說使用make來初始化控件的佈局,使用Frame來更新佈局。固然這一過程不是簡單的在設置值的時候更新一下Frame就能夠的,由於在Cell設置值的時候去更新Frame是沒用的,由於更新完Frame後,在渲染顯示的時候,仍是會按照AutoLayout的佈局來顯示的。咱們須要作的是將Frame佈局放到Autolayout佈局以後,此處咱們要作的就是將更新Frame的相關代碼放到下一個Runloop中來執行。更新Frame代碼以下:
在cell中是make初始化控件佈局,使用Frame更新佈局,和Frame+Frame的方式差很少,只不過是使用Masonry佈局時,在首屏加載的時候不如Frame佈局,之後更新是同樣的。下方是使用Masonry+Frame的形式的Core Animation的結果。效果雖然比上一部分會稍微差一些,可是最終的效果仍是滿Ok的。
3、總結
本篇博客只討論Masonry的佈局方式對FPS的影響,至於上述的NSAttributeString的問題不作過多贅述了。若是根據業務須要,有許多富文本的展現影響了FPS的話,那麼能夠採用其餘的方式來進行優化,好比使用AsynDisplayKit所提供的相關Node進行顯示等等。在博客的結尾,仍是有必要作一個總結的。
下方是咱們在代碼中更爲細化的數據,從數據中不難看出Remake的性能是最差的,因此咱們在使用Masonry時儘可能要少使用Remake。對控件的更新只一味的選擇使用Update也不是一個好的選擇,若是要使用Masonry框架還要對控件進行佈局更新的話,最好是把那些不變的約束和須要更新的約束分開。使用make來添加相關約束,使用update來修改須要更新的約束。固然使用Frame佈局的性能會好一些,不過佈局過程過於繁瑣不便於進行屏幕的適配。固然也可使用Masonry進行佈局使用Frame進行佈局的更新,固然須要注意的是Frame佈局更新的時機,需在Autolayout加載的時機後進行。
下方是進行了統一的數據統計,固然是針對本篇博客所對應的Demo的。下方表格中統計了一次更新cell佈局所採用的不一樣方式的平均時間,從下方的數據中咱們不難看粗Remake的更新佈局用時最多,消耗了12+ms, 而Update全部的約束用時也是很多,一次更新佈局使用了9+ms。而只更新須要更新的佈局用時7+ms, 稍微要比更新全部的佈局要好一些。固然直接修改Frame的用時最少,只用了0.06+ms的時間,從該數據能夠直觀的感覺到Frame佈局的效率性。
而右邊還給出了一個屬性字符串的建立和賦值的用時,其中咱們能夠看到,屬性字符串的建立耗時並非太多,而比較耗時的是屬性字符串的賦值,每次賦值佔用了0.7ms, 若是是10個的話,那麼賦值時間就是7ms, 若是屬性字符串的內容再複雜一些,那麼用時確定會比這個高。固然咱們可使用第三方提供的一些控件和方法將這部分時間給優化掉,這個能夠放到之後再討論。
今天的博客就到這兒吧,目的是在使用Masonry時要合理的進行使用,有必要時,可使用Frame進行佈局。
有問題嗎?更正?請經過加咱們的交流羣 點擊此處進交流羣 ,來一塊兒交流或者發佈您的問題,意見或反饋。
上述demo -github分享連接:github.com/lizelu/FPSP…
做者:青玉伏案