iOS FPS 监控

栏目: IOS · 发布时间: 6年前

内容简介:iOS 系统及其应用以丝般顺滑闻名,界面的顺滑程度对于用户体验至关重要,因此需要针对性地对流程度进行优化。在优化之前必须要找到问题所在,那么就需要解决这两个问题:卡顿的原因是什么?哪里出现了卡顿?YYKit 作者 ibireme 写了在 VSync 信号到来后,系统图形服务会通过

背景

iOS 系统及其应用以丝般顺滑闻名,界面的顺滑程度对于用户体验至关重要,因此需要针对性地对流程度进行优化。在优化之前必须要找到问题所在,那么就需要解决这两个问题:卡顿的原因是什么?哪里出现了卡顿?

卡顿原因

YYKit 作者 ibireme 写了 一篇很好的文章 来解释卡顿问题及解决方法,其中写到卡顿的原因是:

在 VSync 信号到来后,系统图形服务会通过 CADisplayLink 等机制通知 App,App 主线程开始在 CPU 中计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后 CPU 会将计算好的内容提交到 GPU 去,由 GPU 进行变换、合成、渲染。随后 GPU 会把渲染结果提交到帧缓冲区去,等待下一次 VSync 信号到来时显示到屏幕上。由于垂直同步的机制,如果在一个 VSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。

简而言之就是 CPU 和 GPU 的工作量太大,无法在理想的时间内完成。相应的优化策略文章里也写得很清楚。

卡顿监控常见方案

  • FPS 监控:通过 CADisplayLink 来获取每一帧的耗时,进而计算出 FPS。
  • 通过开辟一个子线程监听 runLoop 状态变化来计算停留在各个状态的时间,当 runloop 处于 kCFRunLoopBeforeSourceskCFRunLoopBeforeWaiting 之间的时间过长就可以断定发生了卡顿。

但常见的 FPS 监控存在一些问题。

FPS 监控优化

当界面处于静止状态时,其 PFS 一般都会接近 60,卡顿一般都发生在界面发生滚动时。为了避免界面发生滚动时 FPS 的数据被静止时的数据平均掉,我们需要监听界面的滚动状态。

iOS 的 UIScrollViewDelegate 有三个方法可以做到:

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;	// 用户开始拖动
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;	// 拖动结束
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; 	// 滑动结束

我们只需要 swizzle 这三个方法就可以排除界面静止时的数据。

Swizzling

因为我们要 siwzzle 的是 delegate,跟常见的 swizzle 方法有些不同:

  • 我们并不知道 delegate 的类型。
  • delegate 可能没有实现上面三个方法。

针对一个问题,我们可以直接 hook setDelegate 方法,在 setDelegate 方法内部再 hook UIScrollViewDelegate 的三个方法:

+ (void)load {
    [self sm_swizzleMethod:@selector(setDelegate:) withMethod:@selector(hmfps_setDelegate:)];
}

- (void)hmfps_setDelegate:(id<UIScrollViewDelegate>)delegate {
    NSLog(@"[HMFluencyMonitor] Hook %@", [self class]);
    [self hmfps_hookDelegate:delegate];
    [self hmfps_setDelegate:delegate];
}

但是这样做的好处是可以实现无痕监控,各个页面代码不需要做任何修改;风险是 app 里面的所有 UIScrollView 都会被 hook,包括嵌套的 UIScrollView,范围会比较广,一来 hook 了不需要 hook 的类,二来 crash 风险比较大,也可以提供方法让各个页面自行调用 hook。

- (BOOL)hmfps_shouldSwizzleDelegate:(id _Nonnull)delegate {
    if ([delegate isProxy]) {
        return NO;
    }
    
    if ([self isKindOfClass:[UITextView class]]) {
        return NO;
    }
    
    return YES;
}

对于第二个问题,delegate 可能并没有实现我们要 hook 的三个方法,因此需要为他们增加一个默认的实现,内容是什么都不干。最终的代码如下:

- (void)hmfps_doNothing:(id)nothing {
    // Do nothing
}

+ (void)hmfps_swizzleMethod:(SEL)originalSelector withMethod:(SEL)swizzledSelector forClass:(Class)originalClass{
    Method testMethod = class_getInstanceMethod(originalClass, swizzledSelector);
    if (testMethod) {
        return;
    }

    Method originalMethod = class_getInstanceMethod(originalClass, originalSelector);
    Method swizzledMethod = class_getInstanceMethod([self class], swizzledSelector);
    Method dummyMethod = class_getInstanceMethod([self class], @selector(hmfps_doNothing:));

    class_addMethod(originalClass,
                    originalSelector,
                    method_getImplementation(dummyMethod),
                    method_getTypeEncoding(dummyMethod));

    class_addMethod(originalClass,
                    swizzledSelector,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));

    originalMethod = class_getInstanceMethod(originalClass, originalSelector);
    swizzledMethod = class_getInstanceMethod(originalClass, swizzledSelector);
    method_exchangeImplementations(originalMethod, swizzledMethod);
}

还有另外一个问题,如果项目里面使用了 BlockKit 的 A2DynamicDelegate ,hook 时会发生 crash,真正使用时要进行排除,猜测是因为 A2DynamicDelegate 的基类是 NSProxy 而不是 NSObject 。这里需要业务方自行实现三个 delegate 方法。

Ref


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

游戏引擎架构

游戏引擎架构

[美] Jason Gregory (杰森.格雷戈瑞) / 叶劲峰 / 电子工业出版社 / 2014-1 / 128.00元

《游戏引擎架构》同时涵盖游戏引擎软件开发的理论及实践,并对多方面的题目进行探讨。本书讨论到的概念及技巧实际应用于现实中的游戏工作室,如艺电及顽皮狗。虽然书中采用的例子通常依据一些专门的技术,但是讨论范围远超于某个引擎或API。文中的参考及引用也非常有用,可让读者继续深入游戏开发过程的任何特定方向。 《游戏引擎架构》为一个大学程度的游戏编程课程而编写,但也适合软件工程师、业余爱好者、自学游戏程......一起来看看 《游戏引擎架构》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具