[ WWDC2018 ] - 高效能 AutoLayout High Performance Auto Layout
UICollectionView效能對比,item自動適配大小,iOS 11看上去有掉幀卡頓的現象,iOS 12表現完美,沒有掉幀。
下面是iOS 11和iOS 12的效能對比,灰色條是iOS 11的耗時,藍色條是iOS 12的耗時。在iOS 12上會很大程度改善你的應用程式。
實現和感觀
render loop
render loop 是一個每秒鐘跑120次的一個程式,是為了確保所有的內容都能為每一個frame做好準備。lender loop 一共包括三個步驟來更新約束,佈局和渲染。
- 首先,每一個需要接收到更新約束的view會從子view向上傳遞,直到window
- 然後,每一個接收到的view開始layoutsubviews,和更新約束是從相反的方向開始,layout從window開始到每一個子view進行layout。
- 最後,每一個需要渲染的view,和layout相同,從父view向子view開始渲染。
render loop目的是為了避免重複的工作。
舉一個例子:一個UILable 需要一個約束來描述它的大小,但是有很多屬性會影響他的大小,設定它的font,text size等等都會受到影響。當一個屬性改變的時候,可能text其他屬性也會被重新賦值
,很有可能呼叫一堆屬性的setter方法,這樣效率會很低。
只需要呼叫updateConstraints 並指定好要更新的屬性,render loop會幫助你計算好它的frame並完成渲染,從而避免多次設定的重複工作。
在設定約束的一些不好的寫法,每次開始的時候呼叫deactivate,設定結束之後呼叫activate。相當於layoutsubviews,每次呼叫layoutsubviews你銷燬你subviews,重新建立在重新新增。這樣效能不會很好。
// 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進行佈局。
啟用一個約束
在設定約束的時候發生了什麼事情呢?從下面的圖中可以看到整體的一個結構。
有一個view 在window上,window上面有個叫做engine的內部物件,engine是autolayout計算的核心,當新增一個約束的時候,會建立一個Equation物件,然後會把equation物件新增到engine上,equation依據variables物件。
variables相當於每一個約束的值,比如說一個UIlabel有四個約束minX minY width height那麼minX minY width height 就是variables。
以下面這個圖為例,這裡只關注水平方向的佈局,首先要建立equation,然後每一個equation會新增給engine。
engine會去計算這些variables,engine會把每一個view的variables用數學公式計算出一個定量。
計算出定量之後,engine會傳送通知,通知view呼叫他父view的setNeedsLayout()方法,就會完成render loop的第一步更新約束,然後繼續render loop的 layout更新,最後view會直接拷貝engine計算好的定量進行賦值渲染。
engine是一個layout的快取,和依賴的追蹤器。非常有方向性的,它知道哪些約束會影響哪些view,當你改變一些約束時,它能夠準確的更新。
不需要的約束不要加
你也可以穿過層級,為兩個沒有相同父view的view設定約束,但是這樣效能會很差。
大多數情況下,view的約束應該加在他的父view或者兄弟view上。
最小限度的錯誤
當view向engine獲取約束的值的時候,engine會確保錯誤率最小
構建高效能layout
建立一個layout
構建一個社交軟體的cell,通過autolayout進行佈局。
查詢程式碼中的問題
下面是beta版的一個除錯工具,最上面第一項表示你CPU的使用情況,峰值的地方可能需要關注一下你的layout是否有效能問題,下面一行追蹤你的約束,高的地方說明是有問題的。
第二項是你對約束新增、刪除、修改等操作的記錄。
第三項是當前控制元件的大小。
點選約束峰值的地方可以看詳情。
建立高效能的佈局
通過instrument除錯工具,可以看出一些佈局上的耗時問題。一下是需要注意的幾點:
- 避免刪除所有的約束的情況
- 對於靜態約束,只需要新增一次
- 只改變需要改變的約束
- 儘量用hide() 方法隱藏view,而不是remove然後在add
有些控制元件比較特殊,比如 UIImageView,它的大小是根據他的image計算確定他的content size。UILabel是根據他的text確定的。這些都會返回它們的固有尺寸,UIView 會直接通過他們的固有尺寸來當做約束條件。
重寫 intrinsicContentSize
text的計算是成本很高的,所以UIlabel的size通過text去控制計算開銷成本會很高。這個時候我們可以 通過重寫 UILabel 的 intrinsicContentSize 來直接控制它的固有尺寸。如果已知一個UILabel的展示size,直接重寫其屬性,其他情況使用UIView.noIntrinsicMetric。
override var intrinsicContentSize: CGSize {
return CGSize(width: UIView.noIntrinsicMetric, height: UIView.noIntrinsicMetric)
}
相關文章
- Performance and High-Availability OptionsORMAI
- An Overview of High Performance Computing and Responsibly Reckless AlgorithmsViewORMGo
- The practice of high-performance graph computing system Plato in Nebula GraphORM
- 什麼是 Chrome 開發者工具 performance 皮膚 Experience 裡的 Layout shiftChromeORM
- AutoLayout Tips
- UIScrollView AutoLayoutUIView
- cell label autoLayout
- iOS AutoLayout使用技巧iOS
- WWDC2018 影象最佳實踐
- PerformanceORM
- AutoLayout螢幕適配
- Swift iOS:AutoLayout 快速介紹SwiftiOS
- layout佈局
- Coordinator Layout使用
- MySQL Performance SchemaMySqlORM
- iOS開發之 Autolayout 詳解iOS
- Auto CAD
- Layout的編寫
- Flutter layout 作弊稿Flutter
- 用AutoLayout實現分頁滾動
- 設定performance模式ORM模式
- Performance Without the Event LoopORMOOP
- Boost UDP Transaction PerformanceUDPORM
- webpack Performance: The Comprehensive GuideWebORMGUIIDE
- High Availability (HA) in SQL ServerAISQLServer
- 說說 auto
- auto型別型別
- Yii2 layout 由 controller 向layout中傳遞引數值Controller
- iOS AutoLayout進階(五)UITableViewCell自動高度iOSUIView
- iOS AutoLayout進階(三)Content Compression Resistance PriorityiOS
- MySQL Performance Schema詳解MySqlORM
- [譯] Performance testing of Flutter appsORMFlutterAPP
- 1383. Maximum Performance of a TeamORM
- Performance --- 前端效能監控ORM前端
- Guideline 2.3.10 - Performance - Accurate MetadataGUIIDEORM
- [Vue] Routes auto generatorVue
- Auto關鍵字
- redis:auto-completeRedis