Skip to content

Instantly share code, notes, and snippets.

@drumnkyle
Last active August 17, 2018 18:45
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save drumnkyle/89180f310d705df75647e45dc5f8fd59 to your computer and use it in GitHub Desktop.
KSScrollPerformanceDetector
#import <Foundation/Foundation.h>
/**
These methods will not be called on the main thread. So,
if you will be doing anything with UIKit, ensure you dispatch back
to the main thread.
*/
@protocol KSScrollPerformanceDetectorDelegate<NSObject>
@optional
- (void)framesDropped:(NSInteger)framesDroppedCount cumulativeFramesDropped:(NSInteger)cumulativeFramesDropped cumulativeFrameDropEvents:(NSInteger)cumulativeFrameDropEvents;
@end
@interface KSScrollPerformanceDetector : NSObject
@property(nonatomic, assign, readonly) NSInteger currentFrameDropCount;
@property(nonatomic, assign, readonly) NSInteger currentFrameDropEventCount;
@property(nonatomic, weak, nullable) id<KSScrollPerformanceDetectorDelegate> delegate;
- (nonnull instancetype)init NS_DESIGNATED_INITIALIZER;
- (void)resume;
- (void)pause;
- (void)clearFrameDropCount;
@end
#import "KSScrollPerformanceDetector.h"
#import <UIKit/UIKit.h>
@interface KSScrollPerformanceDetector()
@property(nonatomic, strong, nonnull) CADisplayLink *displayLink;
@property(nonatomic, assign, readwrite) CFTimeInterval lastTimestamp;
@property(nonatomic, assign, readwrite) NSInteger currentFrameDropCount;
@property(nonatomic, assign, readwrite) NSInteger currentFrameDropEventCount;
@property(nonatomic, strong, nonnull) dispatch_queue_t workerQueue;
- (void)displayLinkTriggered:(CADisplayLink *)sender;
- (void)calculateFrameDropForNumberOfFrames:(NSInteger)numberOfFrames;
- (void)registerLifecyleNotifications;
@end
@implementation KSScrollPerformanceDetector
#pragma mark - Initializers
- (instancetype)init {
self = [super init];
[self registerLifecyleNotifications];
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkTriggered:)];
_displayLink.paused = YES;
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
_lastTimestamp = 0;
dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, DISPATCH_QUEUE_PRIORITY_HIGH);
_workerQueue = dispatch_queue_create("com.frame-rate-reporter.worker", attributes);
return self;
}
#pragma mark - Public Methods
- (void)clearFrameDropCount {
__weak typeof(self) weakSelf = self;
dispatch_async(self.workerQueue, ^{
typeof(self) strongSelf = weakSelf;
if (!strongSelf) {
return;
}
self->_currentFrameDropCount = 0;
self->_currentFrameDropEventCount = 0;
});
}
- (void)resume {
self.displayLink.paused = NO;
}
- (void)pause {
self.displayLink.paused = YES;
}
#pragma mark - Private Methods
#pragma mark Lifecyle
- (void)registerLifecyleNotifications {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(notifyActivate:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(notifyDeactivate:)
name:UIApplicationWillResignActiveNotification
object:nil];
}
- (void)notifyActivate:(NSNotification *)notification {
self.displayLink.paused = NO;
self.lastTimestamp = 0;
}
- (void)notifyDeactivate:(NSNotification *)notification {
self.displayLink.paused = YES;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
self.displayLink.paused = YES;
[self.displayLink invalidate];
}
#pragma mark Calculations
- (void)displayLinkTriggered:(CADisplayLink *)sender {
dispatch_async(self.workerQueue, ^{
if (self.lastTimestamp == 0) {
self.lastTimestamp = sender.timestamp;
return;
}
CFTimeInterval duration = sender.duration;
if (duration == 0) {
return;
}
double numberOfFramesDouble = round((sender.timestamp - self.lastTimestamp) / duration);
NSAssert(numberOfFramesDouble <= NSIntegerMax && numberOfFramesDouble >= NSIntegerMin, @"fps double value out of range of NSInteger");
NSInteger numberOfFrames = numberOfFramesDouble;
self.lastTimestamp = sender.timestamp;
[self calculateFrameDropForNumberOfFrames:numberOfFrames];
});
}
- (void)calculateFrameDropForNumberOfFrames:(NSInteger)numberOfFrames {
NSInteger droppedFrameCount = numberOfFrames - 1 > 0 ? numberOfFrames : 0;
self.currentFrameDropCount += droppedFrameCount;
if (droppedFrameCount > 0) {
self.currentFrameDropEventCount += 1;
}
if (droppedFrameCount > 0 &&
self.listener &&
[self.delegate respondsToSelector:@selector(framesDropped:cumulativeFramesDropped:cumulativeFrameDropEvents:)]) {
[self.delegate framesDropped:droppedFrameCount
cumulativeFramesDropped:self.currentFrameDropCount
cumulativeFrameDropEvents:self.currentFrameDropEventCount];
}
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment