检测iOS的APP性能的一些方法

标签: 极客互联 | 发表时间:2016-03-25 23:33 | 作者:shendao
出处:http://www.shellsec.com

首先如果遇到应用卡顿或者因为内存占用过多时一般使用Instruments里的来进行检测。但对于复杂情况可能就需要用到子线程监控主线程的方式来了,下面我对这些方法做些介绍:

Time Profiler

可以查看多个线程里那些方法费时过多的方法。先将右侧Hide System Libraries打上勾,这样能够过滤信息。然后在Call Tree上会默认按照费时的线程进行排序,单个线程中会也会按照对应的费时方法排序,选择方法后能够通过右侧Heaviest Stack Trace里双击查看到具体的费时操作代码,从而能够有针对性的优化,而不需要在一些本来就不会怎么影响性能的地方过度优化。

Allocations

这里可以对每个动作的前后进行Generations,对比内存的增加,查看使内存增加的具体的方法和代码所在位置。具体操作是在右侧Generation Analysis里点击Mark Generation,这样会产生一个Generation,切换到其他页面或一段时间产生了另外一个事件时再点Mark Generation来产生一个新的Generation,这样反复,生成多个Generation,查看这几个Generation会看到Growth的大小,如果太大可以点进去查看相应占用较大的线程里右侧Heaviest Stack Trace里查看对应的代码块,然后进行相应的处理。

Leak

可以在上面区域的Leaks部分看到对应的时间点产生的溢出,选择后在下面区域的Statistics>Allocation Summary能够看到泄漏的对象,同样可以通过Stack Trace查看到具体对应的代码区域。

监控卡顿的方法

还有种方法是在程序里去监控性能问题,这样在上线后可以通过这个程序将用户的卡顿操作记录下来,定时发到自己的服务器上,这样能够更大范围的收集性能问题。众所周知,用户层面感知的卡顿都是来自处理所有UI的主线程上,包括在主线程上进行的大计算,大量的IO操作,或者比较重的绘制工作。如何监控主线程呢,首先需要知道的是主线程和其它线程一样都是靠NSRunLoop来驱动的。可以先看看CFRunLoopRun的大概的逻辑

int32_t __CFRunLoopRun() {     __CFRunLoopDoObservers(KCFRunLoopEntry);     do     {         __CFRunLoopDoObservers(kCFRunLoopBeforeTimers);         __CFRunLoopDoObservers(kCFRunLoopBeforeSources); //这里开始到kCFRunLoopBeforeWaiting之间处理时间是感知卡顿的关键地方          __CFRunLoopDoBlocks();         __CFRunLoopDoSource0(); //处理UI事件          //GCD dispatch main queue         CheckIfExistMessagesInMainDispatchQueue();          //休眠前         __CFRunLoopDoObservers(kCFRunLoopBeforeWaiting);          //等待msg         mach_port_t wakeUpPort = SleepAndWaitForWakingUpPorts();          //等待中          //休眠后,唤醒         __CFRunLoopDoObservers(kCFRunLoopAfterWaiting);          //定时器唤醒         if (wakeUpPort == timerPort)             __CFRunLoopDoTimers();          //异步处理         else if (wakeUpPort == mainDispatchQueuePort)             __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__()          //UI,动画         else             __CFRunLoopDoSource1();          //确保同步         __CFRunLoopDoBlocks();      } while (!stop && !timeout);      //退出RunLoop     __CFRunLoopDoObservers(CFRunLoopExit); }

根据这个RunLoop我们能够通过CFRunLoopObserverRef来度量。用GCD里的dispatch_semaphore_t开启一个新线程,设置一个极限值和出现次数的值,然后获取主线程上在kCFRunLoopBeforeSources到kCFRunLoopBeforeWaiting再到kCFRunLoopAfterWaiting两个状态之间的超过了极限值和出现次数的场景,将堆栈dump下来,最后发到服务器做收集,通过堆栈能够找到对应出问题的那个方法。

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); }  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)         {             //设置30ms连续3次就报卡顿             long semaphoreWait = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 30*NSEC_PER_MSEC));             if (semaphoreWait != 0)             {                 if (activity==kCFRunLoopBeforeSources || activity==kCFRunLoopAfterWaiting)                 {                     if (++timeoutCount < 3)                         continue;                     //dump堆栈,上报服务器的处理放在这                 }             }             timeoutCount = 0;         }     }); }

有时候造成卡顿是因为数据异常,过多,或者过大造成的,亦或者是操作的异常出现的,这样的情况可能在平时日常开发测试中难以遇到,但是在真实的特别是用户受众广的情况下会有人出现,这样这种收集卡顿的方式还是有价值的。

开发时需要注意如何避免一些性能问题

NSDateFormatter

通过Instruments的检测会发现创建NSDateFormatter或者设置NSDateFormatter的属性的耗时总是排在前面,如何处理这个问题呢,比较推荐的是添加属性或者创建静态变量,这样能够使得创建初始化这个次数降到最低。还有就是可以直接用C,或者这个NSData的Category来解决 https://github.com/samsoffes/sstoolkit/blob/master/SSToolkit/NSData%2BSSToolkitAdditions.m

UIImage

这里要主要是会影响内存的开销,需要权衡下imagedNamed和imageWithContentsOfFile,了解两者特性后,在只需要显示一次的图片用后者,这样会减少内存的消耗,但是页面显示会增加Image IO的消耗,这个需要注意下。由于imageWithContentsOfFile不缓存,所以需要在每次页面显示前加载一次,这个IO的操作也是需要考虑权衡的一个点。

页面加载

如果一个页面内容过多,view过多,这样将长页面中的需要滚动才能看到的那个部分视图内容通过开启新的线程同步的加载。

优化首次加载时间

通过Time Profier可以查看到启动所占用的时间,如果太长可以通过Heaviest Stack Trace找到费时的方法进行改造。

转载本站任何文章请注明:转载至神刀安全网,谢谢 神刀安全网 » 检测iOS的APP性能的一些方法

相关 [ios app 性能] 推荐:

[IOS]iOS App性能优化

- - 操作系统 - ITeye博客
iOS App的性能关注点. 虽然iPhone的机能越来越好,但是app的功能也越来越复杂,性能从来都是移动开发的核心关注点之一. 我们说一个app性能好,不是简单指感觉运行速度快,而应该是指应用启动快速、UI反馈响应及时、列表滚动操作流畅、内存使用合理,当然更不能随随便便Crash啦. 工程师开发应用时除了在设计上要避免性能“坑”的出现,在实际遇到“坑”时也要能很快定位原因所在.

检测iOS的APP性能的一些方法

- - 神刀安全网
首先如果遇到应用卡顿或者因为内存占用过多时一般使用Instruments里的来进行检测. 但对于复杂情况可能就需要用到子线程监控主线程的方式来了,下面我对这些方法做些介绍:. 可以查看多个线程里那些方法费时过多的方法. 先将右侧Hide System Libraries打上勾,这样能够过滤信息. 然后在Call Tree上会默认按照费时的线程进行排序,单个线程中会也会按照对应的费时方法排序,选择方法后能够通过右侧Heaviest Stack Trace里双击查看到具体的费时操作代码,从而能够有针对性的优化,而不需要在一些本来就不会怎么影响性能的地方过度优化.

iOS Web App初步

- - 新浪UED
iOS Web App开发,配合HTML5,是目前比较热门的话题. 今天,先抛开HTML5,我们来尝试在PhoneGap框架上进行简单的开发. PhoneGap是一个使用HTML,CSS和JavaScript的,创建移动跨平台移动应用程序的快速开发平台. 它使开发者能够利用iPhone,Android,Palm,Symbian,WP7,Bada和Blackberry等智能手机的核心功能——包括地理定位,加速器,联系人,声音和振动等,此外PhoneGap拥有丰富的插件,可以以此扩展无限的功能.

iOS APP体验设计

- wuwu - 彩程团队BLOG
iOS APP体验设计不像互联网的体验设计那样,有一堆的方法论和可以“借鉴”的案例. 目前除了苹果的<Human Interface Guidelines>和前Palm的<Zen of Palm>外,没有找到更好的设计哲学和方法论. 事实上,即便认真地研读了HIG和Zen of Palm,甚至是Oolon Colluphid的哲学巨作你也无法严格按照Guideline设计出一款出色的APP.

iOS App体验设计

- 志飞 - 36氪
编者按:本文来自成都彩程设计的CTO肖轶翔「@yxshawn」,关注「iOS,人机交互,UED,音乐人,多媒体,哲学,科幻,电子,迷幻」. 他在文中对iOS 应用和普通网站设计进行了对比,并提出了自己的设计理念. iOS APP体验设计不像互联网的体验设计那样,有一堆的方法论和可以“借鉴”的案例. 目前除了苹果的<Human Interface Guidelines>和前Palm的<Zen of Palm>外,没有找到更好的设计哲学和方法论.

iOS App开发那些事

- - IT江湖
自从做Team Leader之后,身上权责发生了变化,于是让我烦恼的不再是具体某个功能,某个界面的实现,而是如何在现有代码的基础上做渐进式的改进,创造出比较合适规范和框架,使得组内成员更快更好地完成任务. 一年下来,颇有点想法,于是啰嗦几句关于iOS App开发的那些事. 首先明确一点,合适的人是指纯技术团队的建设.

The Right Way to Architect iOS App with Swift

- - limboy's HQ
关于 iOS 架构的文章感觉已经泛滥了,前一阵正好 Android 官方推了一套. App Architecture ,于是就在想,对于 iOS 来说,怎样的架构才是最适合的. 这是第一个也是最重要的问题,为什么会出现各种 Architecture Pattern. 我们来想一下,无论是做一个 App 还是搭一套后台系统,如果是一次性的,今天用完明天就可以扔掉,那么怎么快怎么来,代码重复、代码逻辑、代码格式统统不重要.

如何在iOS与Android间移植APP

- plidezus - 雪鸮的啁啾
除了像”I am rich”这种定点打击苹果烧包族的APP外,大多数应用都会尽量覆盖包含尽可能多的用户. 这就需要考虑在iOS和Android两种主流操作系统间移植的问题. 如果为各个平台量身定做界面,就能让用户利用以往的使用习惯快速学习. 但为多个平台设计各异的界面毕竟是需要工作量的. 如何才能在跨平台移植的时候只做那些最有必要的工作呢.