FPSLabel 的使用(檢測tableView的流暢度)

什麼是FPS

FPS :Frames Per Second 的簡稱縮寫,意思是每秒傳輸幀數,可以理解爲我們常說的“刷新率”(單位爲Hz);FPS是測量用於保存、顯示動態視頻的信息數量。每秒鐘幀數愈多,所顯示的畫面就會愈流暢,fps值越低就越卡頓,所以這個值在一定程度上可以衡量應用在圖像繪製渲染處理時的性能。

目前iOS系統中正常的屏幕刷新率爲60Hz(60次每秒),iOS QuartzCore框架中類CADisplayLink 是一個用於顯示的定時器, 它可以讓用戶程序的顯示與屏幕的硬件刷新保持同步。

如何評測界面的流暢度

一般APP當fps平均在40左右的時候我們就要考慮列表滾動的優化啦
卡頓:tableVIew滑動幀數,也就是30FPS左右(30以下用戶可以明顯感覺到屏幕的卡動,可以稱爲垃圾APP了)
流暢:50-60FPS

CADisplayLink

CADisplayLink是一個能讓我們以和屏幕刷新率相同的頻率將內容畫到屏幕上的定時器。
我們在應用中創建一個新的 CADisplayLink 對象,把它添加到一個runloop中,並給它提供一個 target 和selector 在屏幕刷新的時候調用。
利用此原理,我們可以實時獲取列表頁面的FPS。
CADisplayLink和NSTimer會造成循環引用所有我們可以通過runtime消息轉發機制打破循環引用。

代碼如下:創建WeakProxy類集成NSProxy

//創建WeakProxy類集成NSProxy  .h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface WeakProxy : NSProxy
+ (instancetype)proxyWith:(id)target;
@end

// .m
//
//  WeakProxy.m
//  Created by just so so on 2019/9/30.
//  Copyright © 2019 bruce yao. All rights reserved.
//

#import "WeakProxy.h"
@interface WeakProxy()
@property (nonatomic, weak) id target;
@end
@implementation WeakProxy
///類方法 初始化
+ (instancetype)proxyWith:(id)target {
    WeakProxy *proxy = [WeakProxy alloc];
    proxy.target = target;
    return proxy;
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    return self.target;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
    void *null = NULL;
    [invocation getReturnValue:&null];
}
@end

NS_ASSUME_NONNULL_END

創建一個Label的子類用來顯示fps,我們用Swift創建

//
//  FPSLabel.swift
//  DrawLayer
//
//  Created by just so so on 2019/9/30.
//  Copyright © 2019 bruce yao. All rights reserved.
//

import UIKit

class FPSLabel: UILabel {
    //CADisplayLink
    fileprivate var link: CADisplayLink?
    fileprivate var count: UInt = 0
    fileprivate var lastTime: TimeInterval = 0

    override init(frame: CGRect) {
        super.init(frame: frame)
        ///樣式
        layer.cornerRadius = 5
        layer.masksToBounds = true
        backgroundColor = UIColor.init(white: 0, alpha: 0.7)
        font = UIFont.init(name: "Menlo", size: 14)
        self.textAlignment = .center
        ///防止循環引用
        link = CADisplayLink.init(target: WeakProxy.init(self), selector: #selector(tick(_:)))
        ///main runloop 添加到
        link?.add(to: RunLoop.main, forMode: .common)
    }
    deinit {
        link?.invalidate()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
  
  ///滴答滴答
    @objc fileprivate func tick(_ link: CADisplayLink) {
        if lastTime == 0 {
            lastTime = link.timestamp
            return
        }
        count = count + 1
        let delta = link.timestamp - lastTime
        if delta < 1 {
            return
        }
        lastTime = link.timestamp;
        let fps = Double(count) / delta
        count = 0
        let progress = fps / 60.0
        let color = UIColor.init(hue: CGFloat(0.27 * (progress - 0.2)), saturation: 1, brightness: 0.9, alpha: 1)
        self.textColor = color
        self.text = String.init(format: "%.0lf FPS", round(fps))
    }

}

使用

 fileprivate var fpsLabel: FPSLabel = FPSLabel.init(frame: CGRect.zero)
 ///用自動佈局或者直接寫frame在Controller中,填加到View中即可

注意

關於iOS性能優化之屏幕篇,可以參考:https://blog.csdn.net/weixin_38735568/article/details/100893474
關於Timer和CADisplayLink的循環引用原因以及原理詳細介紹,您可以參考:
https://blog.csdn.net/weixin_38735568/article/details/100010612

tableView的其他優化,會不定期更新,敬請期待。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章