iOS性能優化 —— 一個簡單的Layer Rasterize(光柵化)例子

layer的光柵化能夠將繪製完的位圖緩存下來以減小GPU壓力,在合適的時機使用效果十分顯著
layer.shouldRasterize = true
關於光柵化的基礎知識不瞭解的同窗能夠搜一下 百度傳送門緩存

真機測試

  • 測試機型: iPhone X
  • 測試機系統: iOS 13.3.1
  • 測試方法: 在視圖加載出來以後一直將20個Cell使勁滑到底部再立刻滑到頂部,一直重複直至實驗結束
  • 測試視圖:

測試視圖
每一個Cell上有個橙色的 UIView,設置了陰影屬性。 代碼在文章尾部

1. Instruments - Core Animation 對比

shouldRasterize = false

shouldRasterize = true

2. 分析

從圖中第二列幀率來看開啓了光柵化後幀率比較穩定地處於59/60,而使用前的幀率在56/57左右 第三列的GPU使用率就更明顯了,使用後穩定在25%左右,而使用前是83%左右性能優化

討論

看起來光柵化效果很是好,可是若是錯誤地使用可能會致使更大地消耗。
具體某個視圖應不該該開啓,準確的實際測驗最具備說服力,好比用Instruments裏的Core Animatin跑一跑光柵化以後性能是否有提高。
此外,光柵化會緩存,也就意味着它要佔據一些額外的內存,大量的光柵化也會對運行時內存形成必定壓力bash

重要的一點是隻在不頻繁更新 每幀結果 的Layer上開啓,不然會觸發頻繁的緩存刷新。dom

下圖在cellForRowAt方法中動態將上面例子中的陰影顏色設置爲一個隨機值 ide

對比上面例子中開啓光柵化,這裏GPU使用率只提高了2%-3%變可是幀率相比仍是下降了很多。由於雖然避免了每幀都去從新渲染,可是快速滑動的時候,頻繁的觸發了Cell的刷新所帶來的陰影從新繪製以及緩存操做性能

對於性能優化方面來講,若是不知道光柵化好很差用,最簡便的方法是用一張png來顯示(若是這個圖沒有很大的話)
下圖是把動態繪製的陰影背景用一張png代替以後,測試的結果 測試

直接使用圖片

測試代碼

import UIKit

let cellHeight = CGFloat(90)
let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height

class TestViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let contentView = UITableView(frame: UIScreen.main.bounds, style: .grouped)
        contentView.delegate = self
        contentView.dataSource = self
        contentView.register(TestTableCell.self, forCellReuseIdentifier: "Cell")
        
        view.addSubview(contentView)
    }
}

extension TestViewController: UITableViewDelegate, UITableViewDataSource {
    
    func numberOfSections(in tableView: UITableView) -> Int {
         return 1
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20
    }
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return cellHeight
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TestTableCell
        cell.update(title: "Cell \(indexPath.row)")
        return cell
    }
}

class TestTableCell: UITableViewCell {
    
    lazy var blockView: UIView = {
        let view = UIView(frame: CGRect(x: 12, y: 6, width: screenWidth-24, height: cellHeight-12))
        view.layer.shadowColor = UIColor.black.cgColor
        view.layer.shadowOffset = CGSize(width: 10, height: 10)
        view.layer.shadowRadius = 10
        view.layer.shadowOpacity = 1
        view.layer.shouldRasterize = true
        view.backgroundColor = .orange
        return view
    }()
    
    lazy var backgroundImageView: UIImageView = {
        let view = UIImageView(image: UIImage(named: "fit"))
        return view
    }()
    
    lazy var titleLabel: UILabel = {
        let label = UILabel(frame: CGRect.init(x: 12, y: 20, width: 300, height: 20))
        return label
    }()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        contentView.addSubview(blockView)
        contentView.addSubview(titleLabel)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func update(title: String) {
        blockView.layer.shadowColor = Int.random(in: 0...1) == 0 ? UIColor.blue.cgColor : UIColor.orange.cgColor
        titleLabel.text = title
    }
}

複製代碼
相關文章
相關標籤/搜索