转自http://www.tanhao.me/code/151113.html/

在移动设备上开发软件,性能一直是我们最为关心的话题之一,我们作为程序员除了需要努力提高代码质量之外,及时发现和监控软件中那些造成性能低下的”罪魁祸首”也是我们神圣的职责.

众所周知,iOS平台因为UIKit本身的特性,需要将所有的UI操作都放在主线程执行,所以也造成不少程序员都习惯将一些线程安全性不确定的逻辑,以及其它线程结束后的汇总工作等等放到了主线,所以主线程中包含的这些大量计算、IO、绘制都有可能造成卡顿.

在Xcode中已经集成了非常方便的调试工具Instruments,它可以帮助我们在开发测试阶段分析软件运行的性能消耗,但一款软件经过测试流程和实验室分析肯定是不够的,在正式环境中由大量用户在使用过程中监控、分析到的数据更能解决一些隐藏的问题.

寻找卡顿的切入点

监控卡顿,最直接就是找到主线程都在干些啥玩意儿.我们知道一个线程的消息事件处理都是依赖于NSRunLoop来驱动,所以要知道线程正在调用什么方法,就需要从NSRunLoop来入手.CFRunLoop的代码是开源,可以在此处查阅到源代码http://opensource.apple.com/source/CF/CF-1151.16/CFRunLoop.c,其中核心方法CFRunLoopRun简化后的主要逻辑大概是这样的:

1234567891011121314151617181920212223
int32_t __CFRunLoopRun(){//通知即将进入runloop    __CFRunLoopDoObservers(KCFRunLoopEntry);    do { // 通知将要处理timer和source __CFRunLoopDoObservers(kCFRunLoopBeforeTimers); __CFRunLoopDoObservers(kCFRunLoopBeforeSources); __CFRunLoopDoBlocks(); //处理非延迟的主线程调用 __CFRunLoopDoSource0(); //处理UIEvent事件  //GCD dispatch main queue CheckIfExistMessagesInMainDispatchQueue();  // 即将进入休眠 __CFRunLoopDoObservers(kCFRunLoopBeforeWaiting);

 // 等待内核mach_msg事件 mach_port_t wakeUpPort = SleepAndWaitForWakingUpPorts(); // Zzz...

 // 从等待中醒来 __CFRunLoopDoObservers(kCFRunLoopAfterWaiting);

 // 处理因timer的唤醒 if (wakeUpPort == timerPort) __CFRunLoopDoTimers();

 // 处理异步方法唤醒,如dispatch_async else if (wakeUpPort == mainDispatchQueuePort) __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__()

 // UI刷新,动画显示 else __CFRunLoopDoSource1();

 // 再次确保是否有同步的方法需要调用 __CFRunLoopDoBlocks(); } while (!stop && !timeout); //通知即将退出runloop __CFRunLoopDoObservers(CFRunLoopExit);}

不难发现NSRunLoop调用方法主要就是在kCFRunLoopBeforeSources和kCFRunLoopBeforeWaiting之间,还有kCFRunLoopAfterWaiting之后,也就是如果我们发现这两个时间内耗时太长,那么就可以判定出此时主线程卡顿.

量化卡顿的程度

要监控NSRunLoop的状态,我们需要使用到CFRunLoopObserverRef,通过它可以实时获得这些状态值的变化,具体的使用如下:

1234567891011121314151617
static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){ MyClass *object = (__bridge MyClass*)info; object->activity = activity;}

- (void)registerObserver{ CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL}; CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &runLoopObserverCallBack, &context); CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);}

只需要另外再开启一个线程,实时计算这两个状态区域之间的耗时是否到达某个阀值,便能揪出这些性能杀手.

为了让计算更精确,需要让子线程更及时的获知主线程NSRunLoop状态变化,所以dispatch_semaphore_t是个不错的选择,另外卡顿需要覆盖到多次连续小卡顿和单次长时间卡顿两种情景,所以判定条件也需要做适当优化.将上面两个方法添加计算的逻辑如下:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546
static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){ MyClass *object = (__bridge MyClass*)info;

 // 记录状态值 object->activity = activity;

 // 发送信号 dispatch_semaphore_t semaphore = moniotr->semaphore; dispatch_semaphore_signal(semaphore);}

- (void)registerObserver{ CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL}; CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &runLoopObserverCallBack, &context); CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);

 // 创建信号 semaphore = dispatch_semaphore_create(0);

 // 在子线程监控时长 dispatch_async(dispatch_get_global_queue(0, 0), ^{ while (YES) { // 假定连续5次超时50ms认为卡顿(当然也包含了单次超时250ms) long st = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 50*NSEC_PER_MSEC)); if (st != 0) { if (activity==kCFRunLoopBeforeSources || activity==kCFRunLoopAfterWaiting) { if (++timeoutCount < 5) continue;

 NSLog(@"好像有点儿卡哦"); } } timeoutCount = 0; } });}

记录卡顿的函数调用

监控到了卡顿现场,当然下一步便是记录此时的函数调用信息,此处可以使用一个第三方Crash收集组件PLCrashReporter,它不仅可以收集Crash信息也可用于实时获取各线程的调用堆栈,使用示例如下:

12345678910
PLCrashReporterConfig *config = [[PLCrashReporterConfig alloc] initWithSignalHandlerType:PLCrashReporterSignalHandlerTypeBSD                                                                   symbolicationStrategy:PLCrashReporterSymbolicationStrategyAll];PLCrashReporter *crashReporter = [[PLCrashReporter alloc] initWithConfiguration:config];

NSData *data = [crashReporter generateLiveReport];PLCrashReport *reporter = [[PLCrashReport alloc] initWithData:data error:NULL];NSString *report = [PLCrashReportTextFormatter stringValueForCrashReport:reporter withTextFormat:PLCrashReportTextFormatiOS];

NSLog(@"------------\n%@\n------------", report);

当检测到卡顿时,抓取堆栈信息,然后在客户端做一些过滤处理,便可以上报到服务器,通过收集一定量的卡顿数据后经过分析便能准确定位需要优化的逻辑,至此这个实时卡顿监控就大功告成了!

文章示例代码下载:PerformanceMonitor.zip

转载于:https://www.cnblogs.com/gatsbywang/p/5555200.html

【转】iOS实时卡顿监控相关推荐

  1. ios实时卡顿检测和优化方案

    在移动设备上开发软件,性能一直是我们最为关心的话题之一,我们作为程序员除了需要努力提高代码质量之外,及时发现和监控软件中那些造成性能低下的"罪魁祸首"也是我们神圣的职责.友盟+U- ...

  2. 【iOS开发】页面卡顿监控和优化

    转载地址:卡顿 卡顿的原因: 由上面屏幕显示的原理,采用了垂直同步机制的手机设备.如果在一个VSync 时间内,CPU 或GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示 ...

  3. 微信iOS卡顿监控系统

    原创 2015-09-10 guoling WeMobileDev 引子 微信 iOS 团队在值班的时候,时不时会收到这样的卡顿反馈:"用户A 刚才碰到从后台切换前台卡了一下,最近偶尔会遇到 ...

  4. Android UI卡顿监控

    一.背景 应用的使用流畅度,是衡量用户体验的重要标准之一.Android 由于机型配置和系统的不同,项目复杂App场景丰富,代码多人参与迭代历史较久,代码可能会存在很多UI线程耗时的操作,实际测试时候 ...

  5. iOS开发~卡顿优化

    应用卡顿是让人头疼的问题,不像闪退一样直观明了,可以直接通过异常信号或调用栈分析得到,常常让人无处下手.好的用户体验需要我们把细节做到位,画面掉帧会导致卡顿感,造成不好的印象.卡顿是如何造成的,下面详 ...

  6. 广研Android卡顿监控系统

    实现背景 应用的使用流畅度,是衡量用户体验的重要标准之一.Android 由于机型配置和系统的不同,项目复杂App场景丰富,代码多人参与迭代历史较久,代码可能会存在很多UI线程耗时的操作,实际测试时候 ...

  7. 带你打造一套 APM 监控系统 之 卡顿监控

    Python实战社群 Java实战社群 长按识别下方二维码,按需求添加 扫码关注添加客服 进Python社群▲ 扫码关注添加客服 进Java社群▲ 作者丨杭城小刘 来源公众号丨知识小集(zsxjtip ...

  8. Android 线上卡顿监控

    文章目录 1. 卡顿与ANR的关系 2. 卡顿原理 3. 卡顿监控 3.1 WatchDog 3.2 Looper Printer 3.2.1 监控TouchEvent卡顿 3.2.2 监控IdleH ...

  9. iOS 界面卡顿原因

    第一. 界面卡顿的原因 在 VSync[1]信号到来后,系统图形服务会通过 CADisplayLink 等机制通知 App,App 主线程开始在 CPU中计算显示内容,影响因素: 对象创建; 对象调整 ...

最新文章

  1. 【里程碑式证明】同时解决了量子物理学和理论数学的难题!
  2. 反射机制(1)认识Class类
  3. Windows 10小娜只显示文字而不发声怎么办?
  4. reactjs DOM的Diffing算法
  5. 【收藏】IDEA中MAVEN项目自动导入依赖的启动与取消
  6. java web导出excel_Web项目中数据导出Excel的实现方案
  7. 解决extremeComponents中文按拼音排序问题
  8. 【iOS 15】iPhone如何录屏?iPhone屏幕录制技巧分享
  9. python艺术分形数_Python分形盒计数-分形维数
  10. 忆我在北邮四年中的几位老师(全)
  11. 微信小程序学习笔记(三)——两名片小程序实例
  12. MySQL之——数据库的基本操作
  13. 黑帽seo 模板生成php,全自动无限生成关键词页面(黑帽SEO优化终极方法)
  14. MySQL本地安装与配置(超详细,附带安装包)
  15. MediaPlayer简单使用
  16. Linux软件raid删除
  17. 凌骥电源维修GV视频服务器电源维修BPL550AC12-GVG
  18. 2010年南非世界杯八强冠亚军大预测(2010年6月28日)
  19. c语言三只小猪称重案例分析,语言三只小猪教案
  20. oracle表里面空值要填满,漫 谈oracle 中 的 空 值(转)

热门文章

  1. MCMC(二)马尔科夫链
  2. [CVPR 2016] Weakly Supervised Deep Detection Networks论文笔记
  3. 从“兔子狮子谁做老板”的故事,看企业管理
  4. 【贪心】【P5078】Tweetuzki 爱军训
  5. 现在企业会要求JAVA人员会怎样的技术呢?
  6. [转载]datatable中只取前7条数据
  7. 《看透SpringMVC》第十二章 HandlerMapping
  8. centos6.5环境安装zookeeper-3.4.5
  9. 8 基于管道的持久化存储 scrapy
  10. 教你一招超级简单的方法快速搞定grub.conf文件的丢失及损坏