【iOS 印象】性能優化梳理(Swift)

性能監控

  • 業務性能監控:在 App 中業務的開始與結束打點上報,以達到後臺統計監控性能;
  • 卡頓監控:
    • 主線程卡頓監控,經過子線程監測主線程的 runLoop,判斷兩個區域狀態之間的耗時是否達到必定閾值。
    • FPS監控。要保持流暢的UI交互,App 刷新率應該當努力保持在 60fps。監控實現原理比較簡單,經過記錄兩次刷新時間間隔,就能夠計算出當前的 FPS。

內存分配與釋放

  • 基於棧(stack-based)的內存分配

棧是一種很是簡單的數據結構; 數據從棧的頂部推入(push)與彈出(pop); 因爲只可以修改棧的末端,所以能夠經過維護一個指向棧末端的指針來實現這種數據結構;ios

  • 基於堆(heap-based)的內存分配

內存分配具有更加動態的生命週期,更復雜的數據結構 在堆上進行內存分配,須要爲對象開闢並鎖定堆當中的一個空閒塊(free block) 爲了線程安全,必須進行鎖定和同步git

引用計數

引用計數(reference counting)操做自己相對不耗費性能,但因爲使用次數足夠多,所以它帶來的性能影響比較大。引用計數是 Objective-C 和 Swift 中用於肯定什麼時候該釋放對象的安全機制。Swift 當中的引用計數是強制自動管理(ARC, Auto Reference Counting),所以容易被開發者所忽略。github

調度

Swift 中有三種類型的調度(Dispatch)方式:數據庫

  • Swift 會盡量將函數內聯(inline),這樣的話,該函數就能夠直接調用,不會有額外的性能開銷。
  • 靜態調度(static dispatch)本質是經過 V-table 進行的查找和跳轉,這個操做消耗大概 1nm。
  • 動態調度(dynamic dispatch)消耗大概 5nm。只有幾個方法進行這樣的動態調度的話,問題不大。應該避免在嵌套循環或執行次數較多的操做中採用動態調度的方式。

V-table: virtual table,虛函數表,記錄了類中全部虛函數的函數指針,也就是說是個函數指針數組的起始位置。swift

對象

Swift 中有兩種類型的對象:數組

  • 類(Class)
class Index {
	let section: Int
	let item: Int
}

let i = Index(section: 1, item: 2)
let i2 = i
複製代碼

類當中的數據是在堆上分配內存。 Index 這個類包含了兩個屬性,sectionitem,當該對象被建立時,堆上便開闢了sectionitem 的數據空間,並生成一個指向該對象的指針 i。若是對其添加一個引用,則 i2 指向堆上相同區域,這兩個對象指針之間是共用同一塊內存空間的,同時會對該對象自動插入持有操做,引用計數+1。緩存

  • 結構體(Struct)
struct Index {
	let section: Int
	let item: Int
}

let i = Index(section: 1, item: 1)
let i2 = i
複製代碼

一般咱們會說,要編寫性能優異的 Swift 代碼,最簡單的方式儘量使用結構體。安全

結構體儲存在棧上,基於棧進行內存分配,而且一般使用靜態調度或內聯調度。若是將其賦值給另外一個變量 i2,這會將儲存在棧上的值複製一遍產生新的對象,而不是引用。性能優化

另:。枚舉也是值類型,採用枚舉改進數據模型,避免使用大量的字符串。微信

抽象類型,協議會致使性能降低

協議內部會有儲存值的緩存區、元數據,而且要支持動態調度派發,有一個協議記錄表(protocol witness table,也稱做虛函數表),所以協議類型所佔內存要比具體的類、結構體或枚舉要更大。這個能夠簡單瞭解一下,以後考慮另起一篇文章單獨記錄下這個問題。「過早的優化是萬惡之源」,相比面向協議帶來的好處,通常來講能夠不用太過考慮使用協議帶來的細微性能消耗。

即使如此,在面向協議開發的過程當中,也能夠適當地注意如下幾點,在利用協議帶來的好處的同時,避免協議帶來的性能損耗:

  • 若某種協議只用於類,可添加: class做爲約束,採用類協議,如代理(delegate)協議的使用。
  • 將協議做爲泛型約束來使用,而不是單獨做爲類型參數,這樣編譯器能夠對其進行優化。

GCD 進行多線程性能優化

經過 GCD 將一些耗時操做派發到非主線程,提升 UI 流暢度,響應更及時,優化用戶體驗。

I/O 性能優化

  • 使用緩存減小 I/O 次數,NSCache 是專門用來管理緩存的一個類,合理利用緩存可極大提升運行效率。
    • 併發訪問緩存時數據一致性問題
    • 線程安全問題,防止一邊修改一邊遍歷
    • 查找緩存時的性能問題
    • 緩存的釋放與重建,避免無用的緩存佔用過多空間
  • 化零爲整進行寫入操做
  • 選用適合的 API
  • 選用適合的操做線程

減小離屏渲染

離屏渲染:GPU在當前屏幕緩衝區之外新開闢一個緩衝區進行渲染操做。 離屏渲染須要屢次切換上下文環境:先是從當前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結束之後,將離屏緩衝區的渲染結果顯示到屏幕上又須要將上下文環境從離屏切換到當前屏幕,而上下文環境的切換是一項高開銷的動做。

UITableView 性能優化

  • 正確設置 reuseIdentifierUITableViewCell 進行重用
  • 設置統一規格的 Cell
  • 儘可能減小沒必要要的透明視圖
  • 儘可能避免漸變、圖片拉伸與離屏渲染
  • Cell 是動態高度時計算並緩存行高
  • 異步請求加載 Cell 展現數據,並進行預處理,包括圖片的加載、壓縮,富文本的顯示
  • 減小子視圖的層級關係

必要時使用 Autorelease Pool

使用循環體時,考慮是否有必要採用 Autorelease Pool 對臨時對象進行釋放,避免佔用過多內存空間。

合理進行線程分配

合理的線程分配,最終目的就是保證主線程儘可能少地處理非 UI 操做,同時將整個 App 中子線程數量控制在合理範圍,以免沒必要要子線程開啓與切換消耗。

  • UI 與數據源操做在主線程
  • 數據庫操做、日誌記錄、網絡回調在相應的固定線程
  • 不一樣業務,經過建立隊列保證數據一致性

預加載與延遲加載

  • 預處理:耗時操做提早在後臺線程進行處理
  • 延遲加載:必要可視內容優先加載,其餘內容稍後或須要展現時再加載

參考與更多

相關文章
相關標籤/搜索