“时间探测器”
天下武功,唯快不破。很多公司都信奉这个教条.恨不得把app压法周期压缩到最低,这就导致了开发中隐藏了很多问题,有点经验的工程师草率的优化下,更糟的情况那些没有经验的工程师甚至不会对app进行任何优化.
某种程度上来说,你开发过程中是可以忽略性能优化的. 十年前,移动设备的硬件资源是非常有限的.甚至连浮点数都是被禁止的.因为浮点数能导致代码变大计算的速度变慢.
科技发展如此迅速的今天,硬件很大程度上可以弥补软件的短板.现在的移动设备3D硬件处理的效率甚至媲美于PC机了,但是你不能总依赖于硬件和处理器速度来掩饰你APP做的多垃圾吧.(如果安卓系统跑在Iphone上还能够像iOS一样顺滑吗?其实是一个道理的)
性能这个概念很抽线,所以我们必须借助数据化图形化的输出方式.你可能花一周的时间去优化一个有趣的算法,但是这算法只占总执行时间的 0.5%,不管你花多少精力去优化它,没人会注意到.相反一个for循环花费了90%的时间,你稍微修改下就能提高10%的效率,就是这个简单的修改可以 得到大家很大的好感.因为.他们运行app时的第一感受就是比之前快了很多.没人会care你修改的是一个多牛逼的算法,还是一个简单的for循环.
这个说明了什么?
与其花费时间在优化小细节上不如多点时间找到你改优化的地方.
下面引出第一个工具 “时间事件查看器”(自己杜撰的名字英文—Time Profiler),———他可以测量时间的间隔,中断程序执行,跟踪每个线程的堆栈.你可以想象下是xcode调试时按下暂停时的画面
比如,100个样本都在做1毫秒的间隔,然后在某个方法堆栈顶部有10个样本,你可以推算出大概的时间有10%个10毫秒花费在此方法中,这是一个近似值.
废话少说,时间是个检测到的。
从xcode的菜单选择Product-Profile,或者选择?
程序会启动Instruments,这时候你会看到一个选择窗口
这是instruments所有测试仪器的面板,选择 “timer profilter” 点击“profile”回启东模拟器和app,此时会要求你输入一次密码,以便instruments能有权限去截获监听此进程。
在工具窗口中,可以看到时间计数,并留下了一个小箭头移动到右侧的图形在屏幕的中央上方。这表明该应用程序正在运行。
现在开始运行app,搜索一些图片,这时候你发现查找一个结果太慢了,而且搜索结果列表页面滚动起来也是让人无法忍受的。
首先,确保工具栏中的视图选择有选择的所有三个选项,如下所示:
这将确保所有的面板都打开。现在,研究下面的截图和它下面的每个部分的解释:
1. 录控按钮。中间的红色按钮将停止与启动它被点击时,应用程序目前正在分析。注意这实际上是停止和启动应用程序,而不是暂停它。
2. 运行定时器和运行导航,定时器显示APP已经运行了多长时间,箭头之间是可以移动的。如果停止,然后使用录制按钮重新启动应用程序,这将开始一个新的运 行。显示屏便会显示“run2 of 2”,你可以回到第一次运行的数据,首先你停止当前运行,然后按下左箭头回去。
3. 运行轨道。
4. 扩展面板,在时间探查仪器的情况下,它是用来跟踪显示堆栈。
5. 详细地面板。它显示了你正在使用的仪器的主要信息,这是使用频率最高的部门,可以从它这里看到cpu运行的时间
6. 选项面板,稍后介绍。
重头戏来了。
深究
执行图像搜索,并深究结果。我个人比较喜欢寻找“狗”,当然你也可以选择任意你想要的内容。比如猫啊美女啊什么的。
现在上下滚动记下列表,让时间探测器测量下数据,然后注意看下屏幕的变化和数值。这些数值反应了CPU周期。
但是你也许会发现下面的数值太多,看你的眼花缭乱。下面打开左边的调用树 然后按着如下的配置
以下介绍下配置选项:
Separate by Thread: 每个线程应该分开考虑。只有这样你才能揪出那些大量占用CPU的"重"线程
Invert Call Tree: 从上倒下跟踪堆栈,这意味着你看到的表中的方法,将已从第0帧开始取样,这通常你是想要的,只有这样你才能看到CPU中话费时间最深的方法.也就是说 FuncA{FunB{FunC}} 勾选此项后堆栈以C->B-A 把调用层级最深的C显示在最外面
Hide Missing Symbols: 如果dSYM无法找到你的app或者系统框架的话,那么表中看不到方法名只能看到十六进制的数值,如果勾线此项可以隐藏这些符号,便于简化数据
Hide System Libraries: 勾选此项你会显示你app的代码,这是非常有用的. 因为通常你只关心cpu花在自己代码上的时间不是系统上的
Show Obj-C Only: 只显示oc代码 ,如果你的程序是像OpenGl这样的程序,不要勾选侧向因为他有可能是C++的
Flatten Recursion: 递归函数, 每个堆栈跟踪一个条目
Top Functions: 一个函数花费的时间直接在该函数中的总和,以及在函数调用该函数所花费的时间的总时间。因此,如果函数A调用B,那么A的时间报告在A花费的时间加上B. 花费的时间,这非常真有用,因为它可以让你每次下到调用堆栈时挑最大的时间数字,归零在你最耗时的方法。
如果您已启用上述选项,虽然有些值可能会略有不同,下面的结果的顺序应该是类似下表:
通过上面你能看到大部分时间都花在更新表格照片了.
双击此行,然后将会看到如下
那么这很有趣,不是吗!几乎四分之三的时间花费在setPhoto:方法都花在创造照片的图像数据!
现在可以看到的是什么问题,NSData’s dataWithContentsOfURL 方法并不会立即返回,因为要从网上去数据,每次调用都需要长达几秒的时间返回,而此方法运行在主线程,可想而知会有什么结果了.
其实为了解决这个问题,类提供了一个ImageCache 的后台异步下载的方法.
现在,您可以切换到Xcode和手动找到该文件,但仪器有一个方便的“打开Xcode中”按钮,就在你的眼前。找到它的面板只是上面的代码并单击它:
想如下修改
- - (void)setPhoto:(FlickrPhoto *)photo {
- _photo = photo;
- self.textLabel.text = photo.title;f
- // NSData *imageData = [NSData dataWithContentsOfURL:_photo.thumbnailUrl];
- // self.imageView.image = [UIImage imageWithData:imageData];
- [[ImageCache sharedInstance] downloadImageAtURL:_photo.thumbnailUrl
- completionHandler:^(UIImage *image) {
- self.imageView.image = image;
- [self setNeedsLayout];
- }];
- }
修改好厚,在仪器重新运行该应用程序Product—Profile(或cmd-记住,这些快捷键真的会为您节省一些时间)。
请注意,这个时候会再问一次你是否使用一起。这是因为你还有一个窗口中打开这个程序,及仪器假定您要使用相同的选项再次运行。
执行一些更多的搜索,并注意此时用户界面不是那么卡顿了!这些图像现在异步加载,并缓存在后台,所以一旦他们已经被下载一次,他们不必再次下载。
看上去很不错!是时候发布了吗? 当然还不够