UICollectionView性能對比,item自動適配大小,iOS 11看上去有掉幀卡頓的現象,iOS 12表現完美,沒有掉幀。ios
WX20180612-104339.png緩存
下面是iOS 11和iOS 12的性能對比,灰色條是iOS 11的耗時,藍色條是iOS 12的耗時。在iOS 12上會很大程度改善你的應用程序。less
WX20180619-160559.pngide
render loop 是一個每秒鐘跑120次的一個進程,是爲了確保全部的內容都能爲每個frame作好準備。lender loop 一共包括三個步驟來更新約束,佈局和渲染。工具
在這裏我仍是要推薦下我本身建的iOS開發學習羣:680565220,羣裏都是學ios開發的,若是你正在學習ios ,小編歡迎你加入,今天分享的這個案例已經上傳到羣文件,你們都是軟件開發黨,不按期分享乾貨(只有iOS軟件開發相關的),包括我本身整理的一份2018最新的iOS進階資料和高級開發教程oop
WX20180619-160634.png佈局
render loop目的是爲了不重複的工做。
舉一個例子:一個UILable 須要一個約束來描述它的大小,可是有不少屬性會影響他的大小,設置它的font,text size等等都會受到影響。當一個屬性改變的時候,可能text其餘屬性也會被從新賦值
,頗有可能調用一堆屬性的setter方法,這樣效率會很低。
只須要調用updateConstraints 並指定好要更新的屬性,render loop會幫助你計算好它的frame並完成渲染,從而避免屢次設置的重複工做。性能
WX20180619-160709.png學習
在設置約束的一些很差的寫法,每次開始的時候調用deactivate,設置結束以後調用activate。至關於layoutsubviews,每次調用layoutsubviews你銷燬你subviews,從新建立在從新添加。這樣性能不會很好。ui
// Don’t do this! Removes and re-adds constraints potentially at 120 frames per second override func updateConstraints() { NSLayoutConstraint.deactivate(myConstraints) myConstraints.removeAll() let views = ["text1":text1, "text2":text2] myConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[text1]-[text2]", options: [.alignAllFirstBaseline], myConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-[text1]-|", metrics: nil, views: views) options: [], metrics: nil, views: views) NSLayoutConstraint.activate(myConstraints) super.updateConstraints() }
每次都是移除並從新添加,至關於這樣的代碼
// Don’t do this! Removes and re-adds constraints potentially at 120 frames per second override func layoutSubviews() { text1.removeFromSuperview() text1 = nil text1 = UILabel(frame: CGRect(x: 20, y: 20, width: 300, height: 30)) self.addSubview(text1) text2.removeFromSuperview() text2 = nil text2 = UILabel(frame: CGRect(x: 340, y: 20, width: 300, height: 30)) self.addSubview(text2) super.layoutSubviews() }
官方建議寫法爲,約束只須要添加一次,每次調用super.updateConstraints完成約束的更新。
// This is ok! Doesn’t do anything unless self.myConstraints has been nil’d out override func updateConstraints() { if self.myConstraints == nil { var constraints = [NSLayoutConstraint]() let views = ["text1":text1, "text2":text2] constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[text1]-[text2]", options: [.alignAllFirstBaseline], metrics: nil, views: views) constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-[text1]-|", options: [], metrics: nil, views: views) } NSLayoutConstraint.activate(constraints) self.myConstraints = constraints super.updateConstraints() }
render loop有很強的特定性,它的好處能夠避免一些重複性的工做。可是它也很危險,由於它調用的頻率會很高,是很是敏感的一段代碼。
蘋果建議使用interface builder進行佈局。
WX20180619-160810.png
在設置約束的時候發生了什麼事情呢?從下面的圖中能夠看到總體的一個結構。
有一個view 在window上,window上面有個叫作engine的內部對象,engine是autolayout計算的核心,當添加一個約束的時候,會建立一個Equation對象,而後會把equation對象添加到engine上,equation依據variables對象。
WX20180619-160848.png
variables至關於每個約束的值,好比說一個UIlabel有四個約束minX minY width height那麼minX minY width height 就是variables。
WX20180619-160919.png
如下面這個圖爲例,這裏只關注水平方向的佈局,首先要建立equation,而後每個equation會添加給engine。
WX20180619-161418.png
engine會去計算這些variables,engine會把每個view的variables用數學公式計算出一個定量。
WX20180619-161501.png
計算出定量以後,engine會發送通知,通知view調用他父view的setNeedsLayout()方法,就會完成render loop的第一步更新約束,而後繼續render loop的 layout更新,最後view會直接拷貝engine計算好的定量進行賦值渲染。
WX20180619-161533.png
engine是一個layout的緩存,和依賴的追蹤器。很是有方向性的,它知道哪些約束會影響哪些view,當你改變一些約束時,它可以準確的更新。
你也能夠穿過層級,爲兩個沒有相同父view的view設置約束,可是這樣性能會不好。
大多數狀況下,view的約束應該加在他的父view或者兄弟view上。
WX20180619-161603.png
當view向engine獲取約束的值的時候,engine會確保錯誤率最小
WX20180619-161637.png
構建一個社交軟件的cell,經過autolayout進行佈局。
WX20180619-161704.png
下面是beta版的一個調試工具,最上面第一項表示你CPU的使用狀況,峯值的地方可能須要關注一下你的layout是否有性能問題,下面一行追蹤你的約束,高的地方說明是有問題的。
第二項是你對約束添加、刪除、修改等操做的記錄。
第三項是當前控件的大小。
WX20180619-161733.png
點擊約束峯值的地方能夠看詳情。
經過instrument調試工具,能夠看出一些佈局上的耗時問題。一下是須要注意的幾點:
有些控件比較特殊,好比 UIImageView,它的大小是根據他的image計算肯定他的content size。UILabel是根據他的text肯定的。這些都會返回它們的固有尺寸,UIView 會直接經過他們的固有尺寸來當作約束條件。
text的計算是成本很高的,因此UIlabel的size經過text去控制計算開銷成本會很高。這個時候咱們能夠 經過重寫 UILabel 的 intrinsicContentSize 來直接控制它的固有尺寸。若是已知一個UILabel的展現size,直接重寫其屬性,其餘狀況使用UIView.noIntrinsicMetric。
override var intrinsicContentSize: CGSize { return CGSize(width: UIView.noIntrinsicMetric, height: UIVie